erosolar-cli 1.7.346 → 1.7.347
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 +24 -148
- package/dist/bin/erosolar.js +5 -21
- package/dist/bin/erosolar.js.map +1 -1
- package/dist/capabilities/agentSpawningCapability.d.ts.map +1 -1
- package/dist/capabilities/agentSpawningCapability.js +56 -31
- package/dist/capabilities/agentSpawningCapability.js.map +1 -1
- package/dist/contracts/agent-schemas.json +0 -15
- package/dist/contracts/tools.schema.json +0 -9
- package/dist/core/agent.d.ts +2 -2
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js.map +1 -1
- package/dist/core/customCommands.d.ts +1 -0
- package/dist/core/customCommands.d.ts.map +1 -1
- package/dist/core/customCommands.js +3 -0
- package/dist/core/customCommands.js.map +1 -1
- package/dist/core/hooks.d.ts +113 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js +267 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/metricsTracker.d.ts +122 -0
- package/dist/core/metricsTracker.d.ts.map +1 -0
- package/dist/{alpha-zero → core}/metricsTracker.js +2 -5
- package/dist/core/metricsTracker.js.map +1 -0
- package/dist/core/securityAssessment.d.ts +91 -0
- package/dist/core/securityAssessment.d.ts.map +1 -0
- package/dist/core/securityAssessment.js +580 -0
- package/dist/core/securityAssessment.js.map +1 -0
- package/dist/core/sessionStore.d.ts +2 -0
- package/dist/core/sessionStore.d.ts.map +1 -1
- package/dist/core/sessionStore.js +1 -0
- package/dist/core/sessionStore.js.map +1 -1
- package/dist/core/toolPreconditions.d.ts.map +1 -1
- package/dist/core/toolPreconditions.js +0 -14
- package/dist/core/toolPreconditions.js.map +1 -1
- package/dist/core/toolRuntime.d.ts +22 -1
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +0 -5
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/core/toolValidation.d.ts.map +1 -1
- package/dist/core/toolValidation.js +14 -3
- package/dist/core/toolValidation.js.map +1 -1
- package/dist/core/validationRunner.d.ts +1 -3
- package/dist/core/validationRunner.d.ts.map +1 -1
- package/dist/core/validationRunner.js.map +1 -1
- package/dist/core/verification.d.ts +137 -0
- package/dist/core/verification.d.ts.map +1 -0
- package/dist/core/verification.js +323 -0
- package/dist/core/verification.js.map +1 -0
- package/dist/headless/headlessApp.d.ts.map +1 -1
- package/dist/headless/headlessApp.js +21 -0
- package/dist/headless/headlessApp.js.map +1 -1
- package/dist/mcp/sseClient.d.ts.map +1 -1
- package/dist/mcp/sseClient.js +9 -18
- package/dist/mcp/sseClient.js.map +1 -1
- package/dist/plugins/tools/build/buildPlugin.d.ts +0 -6
- package/dist/plugins/tools/build/buildPlugin.d.ts.map +1 -1
- package/dist/plugins/tools/build/buildPlugin.js +4 -10
- package/dist/plugins/tools/build/buildPlugin.js.map +1 -1
- package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
- package/dist/plugins/tools/nodeDefaults.js +0 -2
- package/dist/plugins/tools/nodeDefaults.js.map +1 -1
- package/dist/runtime/agentSession.d.ts +2 -2
- package/dist/runtime/agentSession.d.ts.map +1 -1
- package/dist/runtime/agentSession.js +2 -2
- package/dist/runtime/agentSession.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +43 -7
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +417 -166
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.d.ts +2 -0
- package/dist/shell/shellApp.d.ts.map +1 -1
- package/dist/shell/shellApp.js +82 -9
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/shell/systemPrompt.d.ts.map +1 -1
- package/dist/shell/systemPrompt.js +1 -4
- package/dist/shell/systemPrompt.js.map +1 -1
- package/dist/shell/terminalInput.d.ts +252 -120
- package/dist/shell/terminalInput.d.ts.map +1 -1
- package/dist/shell/terminalInput.js +1117 -541
- package/dist/shell/terminalInput.js.map +1 -1
- package/dist/shell/terminalInputAdapter.d.ts +106 -24
- package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
- package/dist/shell/terminalInputAdapter.js +141 -30
- package/dist/shell/terminalInputAdapter.js.map +1 -1
- package/dist/subagents/agentConfig.d.ts +27 -0
- package/dist/subagents/agentConfig.d.ts.map +1 -0
- package/dist/subagents/agentConfig.js +89 -0
- package/dist/subagents/agentConfig.js.map +1 -0
- package/dist/subagents/agentRegistry.d.ts +33 -0
- package/dist/subagents/agentRegistry.d.ts.map +1 -0
- package/dist/subagents/agentRegistry.js +162 -0
- package/dist/subagents/agentRegistry.js.map +1 -0
- package/dist/subagents/taskRunner.d.ts +7 -1
- package/dist/subagents/taskRunner.d.ts.map +1 -1
- package/dist/subagents/taskRunner.js +180 -47
- package/dist/subagents/taskRunner.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +7 -1
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +42 -18
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/display.d.ts +24 -45
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +140 -259
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/theme.d.ts.map +1 -1
- package/dist/ui/theme.js +6 -8
- package/dist/ui/theme.js.map +1 -1
- package/dist/ui/toolDisplay.d.ts +0 -158
- package/dist/ui/toolDisplay.d.ts.map +1 -1
- package/dist/ui/toolDisplay.js +0 -348
- package/dist/ui/toolDisplay.js.map +1 -1
- package/dist/ui/unified/layout.d.ts +20 -0
- package/dist/ui/unified/layout.d.ts.map +1 -1
- package/dist/ui/unified/layout.js +105 -216
- package/dist/ui/unified/layout.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +10 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +78 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/package.json +4 -4
- package/dist/alpha-zero/agentWrapper.d.ts +0 -84
- package/dist/alpha-zero/agentWrapper.d.ts.map +0 -1
- package/dist/alpha-zero/agentWrapper.js +0 -171
- package/dist/alpha-zero/agentWrapper.js.map +0 -1
- package/dist/alpha-zero/codeEvaluator.d.ts +0 -25
- package/dist/alpha-zero/codeEvaluator.d.ts.map +0 -1
- package/dist/alpha-zero/codeEvaluator.js +0 -273
- package/dist/alpha-zero/codeEvaluator.js.map +0 -1
- package/dist/alpha-zero/competitiveRunner.d.ts +0 -66
- package/dist/alpha-zero/competitiveRunner.d.ts.map +0 -1
- package/dist/alpha-zero/competitiveRunner.js +0 -224
- package/dist/alpha-zero/competitiveRunner.js.map +0 -1
- package/dist/alpha-zero/index.d.ts +0 -67
- package/dist/alpha-zero/index.d.ts.map +0 -1
- package/dist/alpha-zero/index.js +0 -99
- package/dist/alpha-zero/index.js.map +0 -1
- package/dist/alpha-zero/introspection.d.ts +0 -128
- package/dist/alpha-zero/introspection.d.ts.map +0 -1
- package/dist/alpha-zero/introspection.js +0 -300
- package/dist/alpha-zero/introspection.js.map +0 -1
- package/dist/alpha-zero/metricsTracker.d.ts +0 -71
- package/dist/alpha-zero/metricsTracker.d.ts.map +0 -1
- package/dist/alpha-zero/metricsTracker.js.map +0 -1
- package/dist/alpha-zero/security/core.d.ts +0 -125
- package/dist/alpha-zero/security/core.d.ts.map +0 -1
- package/dist/alpha-zero/security/core.js +0 -271
- package/dist/alpha-zero/security/core.js.map +0 -1
- package/dist/alpha-zero/security/google.d.ts +0 -125
- package/dist/alpha-zero/security/google.d.ts.map +0 -1
- package/dist/alpha-zero/security/google.js +0 -311
- package/dist/alpha-zero/security/google.js.map +0 -1
- package/dist/alpha-zero/security/googleLoader.d.ts +0 -17
- package/dist/alpha-zero/security/googleLoader.d.ts.map +0 -1
- package/dist/alpha-zero/security/googleLoader.js +0 -41
- package/dist/alpha-zero/security/googleLoader.js.map +0 -1
- package/dist/alpha-zero/security/index.d.ts +0 -29
- package/dist/alpha-zero/security/index.d.ts.map +0 -1
- package/dist/alpha-zero/security/index.js +0 -32
- package/dist/alpha-zero/security/index.js.map +0 -1
- package/dist/alpha-zero/security/simulation.d.ts +0 -124
- package/dist/alpha-zero/security/simulation.d.ts.map +0 -1
- package/dist/alpha-zero/security/simulation.js +0 -277
- package/dist/alpha-zero/security/simulation.js.map +0 -1
- package/dist/alpha-zero/selfModification.d.ts +0 -109
- package/dist/alpha-zero/selfModification.d.ts.map +0 -1
- package/dist/alpha-zero/selfModification.js +0 -233
- package/dist/alpha-zero/selfModification.js.map +0 -1
- package/dist/alpha-zero/types.d.ts +0 -170
- package/dist/alpha-zero/types.d.ts.map +0 -1
- package/dist/alpha-zero/types.js +0 -31
- package/dist/alpha-zero/types.js.map +0 -1
- package/dist/capabilities/securityTestingCapability.d.ts +0 -13
- package/dist/capabilities/securityTestingCapability.d.ts.map +0 -1
- package/dist/capabilities/securityTestingCapability.js +0 -25
- package/dist/capabilities/securityTestingCapability.js.map +0 -1
- package/dist/core/aiFlowOptimizer.d.ts +0 -26
- package/dist/core/aiFlowOptimizer.d.ts.map +0 -1
- package/dist/core/aiFlowOptimizer.js +0 -31
- package/dist/core/aiFlowOptimizer.js.map +0 -1
- package/dist/core/aiOptimizationEngine.d.ts +0 -158
- package/dist/core/aiOptimizationEngine.d.ts.map +0 -1
- package/dist/core/aiOptimizationEngine.js +0 -428
- package/dist/core/aiOptimizationEngine.js.map +0 -1
- package/dist/core/aiOptimizationIntegration.d.ts +0 -93
- package/dist/core/aiOptimizationIntegration.d.ts.map +0 -1
- package/dist/core/aiOptimizationIntegration.js +0 -250
- package/dist/core/aiOptimizationIntegration.js.map +0 -1
- package/dist/core/enhancedErrorRecovery.d.ts +0 -100
- package/dist/core/enhancedErrorRecovery.d.ts.map +0 -1
- package/dist/core/enhancedErrorRecovery.js +0 -345
- package/dist/core/enhancedErrorRecovery.js.map +0 -1
- package/dist/core/hooksSystem.d.ts +0 -65
- package/dist/core/hooksSystem.d.ts.map +0 -1
- package/dist/core/hooksSystem.js +0 -273
- package/dist/core/hooksSystem.js.map +0 -1
- package/dist/core/memorySystem.d.ts +0 -48
- package/dist/core/memorySystem.d.ts.map +0 -1
- package/dist/core/memorySystem.js +0 -271
- package/dist/core/memorySystem.js.map +0 -1
- package/dist/core/unified/errors.d.ts +0 -189
- package/dist/core/unified/errors.d.ts.map +0 -1
- package/dist/core/unified/errors.js +0 -497
- package/dist/core/unified/errors.js.map +0 -1
- package/dist/core/unified/index.d.ts +0 -19
- package/dist/core/unified/index.d.ts.map +0 -1
- package/dist/core/unified/index.js +0 -68
- package/dist/core/unified/index.js.map +0 -1
- package/dist/core/unified/schema.d.ts +0 -101
- package/dist/core/unified/schema.d.ts.map +0 -1
- package/dist/core/unified/schema.js +0 -350
- package/dist/core/unified/schema.js.map +0 -1
- package/dist/core/unified/toolRuntime.d.ts +0 -179
- package/dist/core/unified/toolRuntime.d.ts.map +0 -1
- package/dist/core/unified/toolRuntime.js +0 -517
- package/dist/core/unified/toolRuntime.js.map +0 -1
- package/dist/core/unified/tools.d.ts +0 -127
- package/dist/core/unified/tools.d.ts.map +0 -1
- package/dist/core/unified/tools.js +0 -1333
- package/dist/core/unified/tools.js.map +0 -1
- package/dist/core/unified/types.d.ts +0 -352
- package/dist/core/unified/types.d.ts.map +0 -1
- package/dist/core/unified/types.js +0 -12
- package/dist/core/unified/types.js.map +0 -1
- package/dist/core/unified/version.d.ts +0 -209
- package/dist/core/unified/version.d.ts.map +0 -1
- package/dist/core/unified/version.js +0 -454
- package/dist/core/unified/version.js.map +0 -1
- package/dist/plugins/tools/security/securityPlugin.d.ts +0 -3
- package/dist/plugins/tools/security/securityPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/security/securityPlugin.js +0 -12
- package/dist/plugins/tools/security/securityPlugin.js.map +0 -1
- package/dist/security/active-stack-security.d.ts +0 -112
- package/dist/security/active-stack-security.d.ts.map +0 -1
- package/dist/security/active-stack-security.js +0 -296
- package/dist/security/active-stack-security.js.map +0 -1
- package/dist/security/advanced-persistence-research.d.ts +0 -92
- package/dist/security/advanced-persistence-research.d.ts.map +0 -1
- package/dist/security/advanced-persistence-research.js +0 -195
- package/dist/security/advanced-persistence-research.js.map +0 -1
- package/dist/security/advanced-targeting.d.ts +0 -119
- package/dist/security/advanced-targeting.d.ts.map +0 -1
- package/dist/security/advanced-targeting.js +0 -233
- package/dist/security/advanced-targeting.js.map +0 -1
- package/dist/security/assessment/vulnerabilityAssessment.d.ts +0 -104
- package/dist/security/assessment/vulnerabilityAssessment.d.ts.map +0 -1
- package/dist/security/assessment/vulnerabilityAssessment.js +0 -315
- package/dist/security/assessment/vulnerabilityAssessment.js.map +0 -1
- package/dist/security/authorization/securityAuthorization.d.ts +0 -88
- package/dist/security/authorization/securityAuthorization.d.ts.map +0 -1
- package/dist/security/authorization/securityAuthorization.js +0 -172
- package/dist/security/authorization/securityAuthorization.js.map +0 -1
- package/dist/security/comprehensive-targeting.d.ts +0 -85
- package/dist/security/comprehensive-targeting.d.ts.map +0 -1
- package/dist/security/comprehensive-targeting.js +0 -438
- package/dist/security/comprehensive-targeting.js.map +0 -1
- package/dist/security/global-security-integration.d.ts +0 -91
- package/dist/security/global-security-integration.d.ts.map +0 -1
- package/dist/security/global-security-integration.js +0 -218
- package/dist/security/global-security-integration.js.map +0 -1
- package/dist/security/index.d.ts +0 -38
- package/dist/security/index.d.ts.map +0 -1
- package/dist/security/index.js +0 -47
- package/dist/security/index.js.map +0 -1
- package/dist/security/persistence-analyzer.d.ts +0 -56
- package/dist/security/persistence-analyzer.d.ts.map +0 -1
- package/dist/security/persistence-analyzer.js +0 -187
- package/dist/security/persistence-analyzer.js.map +0 -1
- package/dist/security/persistence-cli.d.ts +0 -36
- package/dist/security/persistence-cli.d.ts.map +0 -1
- package/dist/security/persistence-cli.js +0 -160
- package/dist/security/persistence-cli.js.map +0 -1
- package/dist/security/persistence-research.d.ts +0 -92
- package/dist/security/persistence-research.d.ts.map +0 -1
- package/dist/security/persistence-research.js +0 -364
- package/dist/security/persistence-research.js.map +0 -1
- package/dist/security/research/persistenceResearch.d.ts +0 -97
- package/dist/security/research/persistenceResearch.d.ts.map +0 -1
- package/dist/security/research/persistenceResearch.js +0 -282
- package/dist/security/research/persistenceResearch.js.map +0 -1
- package/dist/security/security-integration.d.ts +0 -74
- package/dist/security/security-integration.d.ts.map +0 -1
- package/dist/security/security-integration.js +0 -137
- package/dist/security/security-integration.js.map +0 -1
- package/dist/security/security-testing-framework.d.ts +0 -112
- package/dist/security/security-testing-framework.d.ts.map +0 -1
- package/dist/security/security-testing-framework.js +0 -364
- package/dist/security/security-testing-framework.js.map +0 -1
- package/dist/security/simulation/attackSimulation.d.ts +0 -93
- package/dist/security/simulation/attackSimulation.d.ts.map +0 -1
- package/dist/security/simulation/attackSimulation.js +0 -341
- package/dist/security/simulation/attackSimulation.js.map +0 -1
- package/dist/security/strategic-operations.d.ts +0 -100
- package/dist/security/strategic-operations.d.ts.map +0 -1
- package/dist/security/strategic-operations.js +0 -276
- package/dist/security/strategic-operations.js.map +0 -1
- package/dist/security/tool-security-wrapper.d.ts +0 -58
- package/dist/security/tool-security-wrapper.d.ts.map +0 -1
- package/dist/security/tool-security-wrapper.js +0 -156
- package/dist/security/tool-security-wrapper.js.map +0 -1
- package/dist/shell/claudeCodeStreamHandler.d.ts +0 -145
- package/dist/shell/claudeCodeStreamHandler.d.ts.map +0 -1
- package/dist/shell/claudeCodeStreamHandler.js +0 -322
- package/dist/shell/claudeCodeStreamHandler.js.map +0 -1
- package/dist/shell/inputQueueManager.d.ts +0 -144
- package/dist/shell/inputQueueManager.d.ts.map +0 -1
- package/dist/shell/inputQueueManager.js +0 -290
- package/dist/shell/inputQueueManager.js.map +0 -1
- package/dist/shell/metricsTracker.d.ts +0 -60
- package/dist/shell/metricsTracker.d.ts.map +0 -1
- package/dist/shell/metricsTracker.js +0 -119
- package/dist/shell/metricsTracker.js.map +0 -1
- package/dist/shell/streamingOutputManager.d.ts +0 -115
- package/dist/shell/streamingOutputManager.d.ts.map +0 -1
- package/dist/shell/streamingOutputManager.js +0 -225
- package/dist/shell/streamingOutputManager.js.map +0 -1
- package/dist/tools/securityTools.d.ts +0 -22
- package/dist/tools/securityTools.d.ts.map +0 -1
- package/dist/tools/securityTools.js +0 -448
- package/dist/tools/securityTools.js.map +0 -1
- package/dist/ui/persistentPrompt.d.ts +0 -50
- package/dist/ui/persistentPrompt.d.ts.map +0 -1
- package/dist/ui/persistentPrompt.js +0 -92
- package/dist/ui/persistentPrompt.js.map +0 -1
- package/dist/ui/terminalUISchema.d.ts +0 -195
- package/dist/ui/terminalUISchema.d.ts.map +0 -1
- package/dist/ui/terminalUISchema.js +0 -113
- package/dist/ui/terminalUISchema.js.map +0 -1
- package/scripts/deploy-security-capabilities.js +0 -178
|
@@ -2,7 +2,7 @@ import { stdin as input, stdout as output, exit } from 'node:process';
|
|
|
2
2
|
import { exec } from 'node:child_process';
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
4
|
import { display } from '../ui/display.js';
|
|
5
|
-
import { theme
|
|
5
|
+
import { theme } from '../ui/theme.js';
|
|
6
6
|
import { getContextWindowTokens } from '../core/contextWindow.js';
|
|
7
7
|
import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
|
|
8
8
|
import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, } from '../core/preferences.js';
|
|
@@ -19,11 +19,11 @@ import { SkillRepository } from '../skills/skillRepository.js';
|
|
|
19
19
|
import { createSkillTools } from '../tools/skillTools.js';
|
|
20
20
|
import { FileChangeTracker } from './fileChangeTracker.js';
|
|
21
21
|
import { formatShortcutsHelp } from '../ui/shortcutsHelp.js';
|
|
22
|
-
import { MetricsTracker } from '
|
|
22
|
+
import { MetricsTracker } from '../core/metricsTracker.js';
|
|
23
23
|
import { listAvailablePlugins } from '../plugins/index.js';
|
|
24
|
-
import { loadMemory, listMemoryPaths, getDefaultProjectMemoryPath, getUserMemoryEditPath, } from '../core/memorySystem.js';
|
|
25
24
|
import { TerminalInputAdapter } from './terminalInputAdapter.js';
|
|
26
|
-
import {
|
|
25
|
+
import { renderSessionFrame } from '../ui/unified/layout.js';
|
|
26
|
+
import { isUpdateInProgress, maybeOfferCliUpdate } from './updateManager.js';
|
|
27
27
|
import { writeLock } from '../ui/writeLock.js';
|
|
28
28
|
import { enterStreamingMode, exitStreamingMode } from '../ui/globalWriteLock.js';
|
|
29
29
|
const execAsync = promisify(exec);
|
|
@@ -35,6 +35,7 @@ const DROPDOWN_COLORS = [
|
|
|
35
35
|
theme.success,
|
|
36
36
|
theme.warning,
|
|
37
37
|
];
|
|
38
|
+
const STREAMING_SPINNER_FRAMES = ['◐', '◓', '◑', '◒'];
|
|
38
39
|
// Load MODEL_PRESETS from centralized schema
|
|
39
40
|
const MODEL_PRESETS = getModels().map((model) => ({
|
|
40
41
|
id: model.id,
|
|
@@ -49,11 +50,13 @@ const MODEL_PRESETS = getModels().map((model) => ({
|
|
|
49
50
|
const BASE_SLASH_COMMANDS = getSlashCommands().map((cmd) => ({
|
|
50
51
|
command: cmd.command,
|
|
51
52
|
description: cmd.description,
|
|
53
|
+
category: cmd.category,
|
|
52
54
|
}));
|
|
53
55
|
// Load PROVIDER_LABELS from centralized schema
|
|
54
56
|
const PROVIDER_LABELS = Object.fromEntries(getProviders().map((provider) => [provider.id, provider.label]));
|
|
55
57
|
// Allow enough time for paste detection to kick in before flushing buffered lines
|
|
56
58
|
const CONTEXT_USAGE_THRESHOLD = 0.9;
|
|
59
|
+
const CONTEXT_AUTOCOMPACT_PERCENT = Math.round(CONTEXT_USAGE_THRESHOLD * 100);
|
|
57
60
|
const CONTEXT_RECENT_MESSAGE_COUNT = 12;
|
|
58
61
|
const CONTEXT_CLEANUP_CHARS_PER_CHUNK = 6000;
|
|
59
62
|
const CONTEXT_CLEANUP_MAX_OUTPUT_TOKENS = 800;
|
|
@@ -94,11 +97,12 @@ export class InteractiveShell {
|
|
|
94
97
|
uiAdapter;
|
|
95
98
|
uiUpdates;
|
|
96
99
|
_fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
|
|
97
|
-
|
|
100
|
+
alphaZeroMetrics; // Alpha Zero 2 performance tracking
|
|
98
101
|
statusSubscription = null;
|
|
99
102
|
followUpQueue = [];
|
|
100
103
|
isDrainingQueue = false;
|
|
101
104
|
activeContextWindowTokens = null;
|
|
105
|
+
latestTokenUsage = { used: null, limit: null };
|
|
102
106
|
sessionPreferences;
|
|
103
107
|
autosaveEnabled;
|
|
104
108
|
autoContinueEnabled;
|
|
@@ -129,6 +133,9 @@ export class InteractiveShell {
|
|
|
129
133
|
statusLineState = null;
|
|
130
134
|
statusMessageOverride = null;
|
|
131
135
|
promptRefreshTimer = null;
|
|
136
|
+
launchPaletteShown = false;
|
|
137
|
+
version;
|
|
138
|
+
alternateScreenEnabled;
|
|
132
139
|
constructor(config) {
|
|
133
140
|
this.profile = config.profile;
|
|
134
141
|
this.profileLabel = config.profileLabel;
|
|
@@ -142,6 +149,8 @@ export class InteractiveShell {
|
|
|
142
149
|
this.autoContinueEnabled = this.sessionPreferences.autoContinue;
|
|
143
150
|
this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
|
|
144
151
|
this._enabledPlugins = config.enabledPlugins ?? [];
|
|
152
|
+
this.version = config.version ?? '0.0.0';
|
|
153
|
+
this.alternateScreenEnabled = config.alternateScreen ?? true;
|
|
145
154
|
this.initializeSessionHistory();
|
|
146
155
|
this.sessionState = {
|
|
147
156
|
provider: config.initialModel.provider,
|
|
@@ -162,6 +171,7 @@ export class InteractiveShell {
|
|
|
162
171
|
this.slashCommands.push({
|
|
163
172
|
command: '/agents',
|
|
164
173
|
description: 'Select the default agent profile (applies on next launch)',
|
|
174
|
+
category: 'configuration',
|
|
165
175
|
});
|
|
166
176
|
}
|
|
167
177
|
this.customCommands = loadCustomSlashCommands();
|
|
@@ -170,18 +180,21 @@ export class InteractiveShell {
|
|
|
170
180
|
this.slashCommands.push({
|
|
171
181
|
command: custom.command,
|
|
172
182
|
description: `${custom.description} (custom)`,
|
|
183
|
+
category: custom.category ?? 'other',
|
|
173
184
|
});
|
|
174
185
|
}
|
|
175
186
|
if (!this.slashCommands.some((cmd) => cmd.command === '/exit')) {
|
|
176
187
|
this.slashCommands.push({
|
|
177
188
|
command: '/exit',
|
|
178
189
|
description: 'Quit the CLI immediately',
|
|
190
|
+
category: 'other',
|
|
179
191
|
});
|
|
180
192
|
}
|
|
181
193
|
// Add /plugins command
|
|
182
194
|
this.slashCommands.push({
|
|
183
195
|
command: '/plugins',
|
|
184
196
|
description: 'Show available and loaded plugins',
|
|
197
|
+
category: 'configuration',
|
|
185
198
|
});
|
|
186
199
|
this.statusTracker = config.statusTracker;
|
|
187
200
|
this.ui = config.ui;
|
|
@@ -213,20 +226,28 @@ export class InteractiveShell {
|
|
|
213
226
|
onEditModeChange: (mode) => this.handleEditModeChange(mode),
|
|
214
227
|
onToggleVerify: () => this.toggleVerificationMode(),
|
|
215
228
|
onToggleAutoContinue: () => this.toggleAutoContinueMode(),
|
|
229
|
+
onToggleThinking: () => this.cycleThinkingMode(),
|
|
230
|
+
onClearContext: () => this.handleClearContext(),
|
|
216
231
|
});
|
|
217
|
-
// Register output interceptor for cursor positioning during streaming
|
|
218
|
-
this.terminalInput.registerOutputInterceptor(display);
|
|
219
|
-
// Set banner content to be written during unified UI initialization
|
|
220
|
-
this.terminalInput.setBannerContent(display.getBannerContent());
|
|
221
|
-
// Initialize unified UI - clears screen, sets up scroll region, writes banner,
|
|
222
|
-
// and renders input area at bottom
|
|
223
|
-
this.terminalInput.initializeUnifiedUI();
|
|
224
232
|
// Initialize Alpha Zero 2 metrics tracking
|
|
225
|
-
this.
|
|
233
|
+
this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
|
|
226
234
|
this.setupStatusTracking();
|
|
227
235
|
this.refreshContextGauge();
|
|
236
|
+
// Start terminal input (sets up handlers)
|
|
228
237
|
this.terminalInput.start();
|
|
238
|
+
// Enter alternate screen buffer when enabled, otherwise clear the main screen for layout
|
|
239
|
+
if (this.alternateScreenEnabled) {
|
|
240
|
+
this.terminalInput.enterAlternateScreen();
|
|
241
|
+
}
|
|
242
|
+
else if (output.isTTY) {
|
|
243
|
+
this.terminalInput.clearScreen();
|
|
244
|
+
}
|
|
245
|
+
// Stream banner first - this sets up scroll region dynamically
|
|
246
|
+
const banner = this.buildBanner();
|
|
247
|
+
this.terminalInput.streamContent(banner + '\n\n');
|
|
248
|
+
// Render chat box after banner is streamed
|
|
229
249
|
this.refreshControlBar();
|
|
250
|
+
this.terminalInput.forceRender();
|
|
230
251
|
this.rebuildAgent();
|
|
231
252
|
this.setupHandlers();
|
|
232
253
|
this.refreshBannerSessionInfo();
|
|
@@ -246,6 +267,10 @@ export class InteractiveShell {
|
|
|
246
267
|
this.activeSessionId = stored.id;
|
|
247
268
|
this.activeSessionTitle = stored.title;
|
|
248
269
|
this.sessionResumeNotice = `Resumed session "${stored.title}".`;
|
|
270
|
+
// Restore scrollback buffer if available
|
|
271
|
+
if (stored.scrollbackBuffer && Array.isArray(stored.scrollbackBuffer)) {
|
|
272
|
+
this.terminalInput.loadScrollbackBuffer(stored.scrollbackBuffer);
|
|
273
|
+
}
|
|
249
274
|
return;
|
|
250
275
|
}
|
|
251
276
|
display.showWarning(`Session "${this.sessionRestoreConfig.sessionId}" not found. Starting fresh session.`);
|
|
@@ -259,6 +284,10 @@ export class InteractiveShell {
|
|
|
259
284
|
this.activeSessionId = null;
|
|
260
285
|
this.activeSessionTitle = autosave.title;
|
|
261
286
|
this.sessionResumeNotice = 'Restored last autosaved session.';
|
|
287
|
+
// Restore scrollback buffer if available
|
|
288
|
+
if (autosave.scrollbackBuffer && Array.isArray(autosave.scrollbackBuffer)) {
|
|
289
|
+
this.terminalInput.loadScrollbackBuffer(autosave.scrollbackBuffer);
|
|
290
|
+
}
|
|
262
291
|
return;
|
|
263
292
|
}
|
|
264
293
|
display.showWarning('No autosaved session found. Starting fresh session.');
|
|
@@ -273,14 +302,24 @@ export class InteractiveShell {
|
|
|
273
302
|
this.sessionResumeNotice = null;
|
|
274
303
|
}
|
|
275
304
|
async start(initialPrompt) {
|
|
305
|
+
// Check for updates in background (non-blocking)
|
|
306
|
+
if (this.version) {
|
|
307
|
+
void maybeOfferCliUpdate(this.version);
|
|
308
|
+
}
|
|
276
309
|
if (initialPrompt) {
|
|
277
310
|
this.logUserPrompt(initialPrompt);
|
|
278
311
|
await this.processInputBlock(initialPrompt);
|
|
279
312
|
return;
|
|
280
313
|
}
|
|
314
|
+
this.showLaunchCommandPalette();
|
|
281
315
|
// Ensure the terminal input is visible
|
|
282
316
|
this.terminalInput.render();
|
|
283
317
|
}
|
|
318
|
+
showLaunchCommandPalette() {
|
|
319
|
+
// Disabled: Quick commands palette takes up too much space
|
|
320
|
+
// Users can type /help to see available commands
|
|
321
|
+
this.launchPaletteShown = true;
|
|
322
|
+
}
|
|
284
323
|
/**
|
|
285
324
|
* TerminalInputAdapter submit handler
|
|
286
325
|
*/
|
|
@@ -294,9 +333,8 @@ export class InteractiveShell {
|
|
|
294
333
|
this.handleInputChange('');
|
|
295
334
|
return;
|
|
296
335
|
}
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
this.terminalInput.setStreaming(true);
|
|
336
|
+
// DON'T clear the input here - keep it visible while streaming.
|
|
337
|
+
// The input will be cleared after streaming completes in the finally block.
|
|
300
338
|
this.logUserPrompt(approved);
|
|
301
339
|
void this.processInputBlock(approved).catch((err) => {
|
|
302
340
|
display.showError(err instanceof Error ? err.message : String(err), err);
|
|
@@ -386,6 +424,59 @@ export class InteractiveShell {
|
|
|
386
424
|
: 'The model will not be auto-prompted to continue.') +
|
|
387
425
|
' Toggle with alt+c.');
|
|
388
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* Cycle through thinking modes (Alt+T keyboard shortcut).
|
|
429
|
+
*/
|
|
430
|
+
cycleThinkingMode() {
|
|
431
|
+
const modes = ['concise', 'balanced', 'extended'];
|
|
432
|
+
const currentIndex = modes.indexOf(this.thinkingMode);
|
|
433
|
+
const nextIndex = (currentIndex + 1) % modes.length;
|
|
434
|
+
const nextMode = modes[nextIndex];
|
|
435
|
+
this.thinkingMode = nextMode;
|
|
436
|
+
saveSessionPreferences({ thinkingMode: this.thinkingMode });
|
|
437
|
+
this.refreshControlBar();
|
|
438
|
+
const descriptions = {
|
|
439
|
+
concise: 'Minimal reasoning, faster responses',
|
|
440
|
+
balanced: 'Default reasoning depth',
|
|
441
|
+
extended: 'Deep reasoning, thorough analysis',
|
|
442
|
+
};
|
|
443
|
+
display.showInfo(`Thinking mode: ${theme.info(nextMode)} - ${descriptions[nextMode]}. (Alt+T to cycle)`);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Handle context clear/compact request (Alt+X keyboard shortcut).
|
|
447
|
+
*/
|
|
448
|
+
handleClearContext() {
|
|
449
|
+
if (this.isProcessing) {
|
|
450
|
+
display.showWarning('Cannot clear context while processing. Wait for completion or press Ctrl+C.');
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
// Trigger context compaction
|
|
454
|
+
display.showInfo('Compacting context... This will summarize the conversation and free up space.');
|
|
455
|
+
void this.performContextCompaction();
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Perform context compaction by summarizing conversation history.
|
|
459
|
+
*/
|
|
460
|
+
async performContextCompaction() {
|
|
461
|
+
try {
|
|
462
|
+
// For now, just clear the history and show a message
|
|
463
|
+
// A full implementation would summarize the conversation
|
|
464
|
+
const oldLength = this.cachedHistory.length;
|
|
465
|
+
if (oldLength === 0) {
|
|
466
|
+
display.showInfo('Context is already empty.');
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// Keep the last few messages for continuity
|
|
470
|
+
const keepCount = Math.min(4, oldLength);
|
|
471
|
+
this.cachedHistory = this.cachedHistory.slice(-keepCount);
|
|
472
|
+
display.showSuccess(`Context compacted: ${oldLength} messages reduced to ${keepCount}. ` +
|
|
473
|
+
`Context usage reset. Continue your conversation.`);
|
|
474
|
+
this.refreshControlBar();
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
display.showError('Failed to compact context', error);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
389
480
|
/**
|
|
390
481
|
* Gate submissions when edit permission mode is active.
|
|
391
482
|
* Returns the text to send when approved, or null when waiting for confirmation.
|
|
@@ -485,6 +576,10 @@ export class InteractiveShell {
|
|
|
485
576
|
return;
|
|
486
577
|
}
|
|
487
578
|
this.shuttingDown = true;
|
|
579
|
+
const shouldRestoreScrollback = this.alternateScreenEnabled && this.terminalInput.isAlternateScreenActive();
|
|
580
|
+
const scrollbackSnapshot = shouldRestoreScrollback
|
|
581
|
+
? this.terminalInput.getScrollbackSnapshot()
|
|
582
|
+
: null;
|
|
488
583
|
// Stop any active spinner to prevent process hang
|
|
489
584
|
display.stopThinking(false);
|
|
490
585
|
this.stopStreamingHeartbeat();
|
|
@@ -493,18 +588,34 @@ export class InteractiveShell {
|
|
|
493
588
|
this.teardownStatusTracking();
|
|
494
589
|
// Clear any pending cleanup to prevent hanging
|
|
495
590
|
this.pendingCleanup = null;
|
|
591
|
+
// Reset terminal state before disposing adapters
|
|
592
|
+
this.terminalInput.exitStreamingScrollRegion();
|
|
593
|
+
if (this.alternateScreenEnabled) {
|
|
594
|
+
this.terminalInput.exitAlternateScreen();
|
|
595
|
+
}
|
|
496
596
|
// Dispose terminal input handler
|
|
497
597
|
this.terminalInput.dispose();
|
|
498
598
|
// Dispose unified UI adapter
|
|
499
599
|
this.uiAdapter.dispose();
|
|
600
|
+
if (scrollbackSnapshot && scrollbackSnapshot.length > 0) {
|
|
601
|
+
this.restoreScrollbackSnapshot(scrollbackSnapshot);
|
|
602
|
+
}
|
|
500
603
|
display.newLine();
|
|
501
|
-
|
|
502
|
-
console.log(theme.
|
|
503
|
-
console.log(
|
|
504
|
-
console.log(` ${theme.ui.muted('Support:')} ${theme.info('support@ero.solar')}`);
|
|
505
|
-
console.log(theme.gradient.warm('━'.repeat(50)));
|
|
604
|
+
console.log(theme.ui.muted('━'.repeat(44)));
|
|
605
|
+
console.log(theme.ui.muted(' Goodbye! · support@ero.solar'));
|
|
606
|
+
console.log(theme.ui.muted('━'.repeat(44)));
|
|
506
607
|
exit(0);
|
|
507
608
|
}
|
|
609
|
+
restoreScrollbackSnapshot(lines) {
|
|
610
|
+
if (!lines.length) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const transcript = lines.join('\n');
|
|
614
|
+
const separator = theme.ui.muted('─'.repeat(44));
|
|
615
|
+
const header = theme.ui.muted('Restored scrollback from this session:');
|
|
616
|
+
// Write directly to stdout after exiting alternate screen to preserve the transcript
|
|
617
|
+
process.stdout.write(`\n${separator}\n${header}\n${transcript}\n${separator}\n`);
|
|
618
|
+
}
|
|
508
619
|
/**
|
|
509
620
|
* Update status bar message
|
|
510
621
|
*/
|
|
@@ -672,13 +783,14 @@ export class InteractiveShell {
|
|
|
672
783
|
});
|
|
673
784
|
}
|
|
674
785
|
setProcessingStatus(detail) {
|
|
786
|
+
this.latestTokenUsage = { used: null, limit: this.latestTokenUsage.limit };
|
|
675
787
|
this.statusTracker.setBase('Working on your request', {
|
|
676
788
|
detail: this.describeStatusDetail(detail),
|
|
677
789
|
tone: 'info',
|
|
678
790
|
});
|
|
679
791
|
}
|
|
680
792
|
describeStatusDetail(detail) {
|
|
681
|
-
const parts =
|
|
793
|
+
const parts = detail?.trim() ? [detail.trim()] : [];
|
|
682
794
|
const queued = this.followUpQueue.length;
|
|
683
795
|
if (queued > 0) {
|
|
684
796
|
parts.push(`${queued} follow-up${queued === 1 ? '' : 's'} queued`);
|
|
@@ -691,12 +803,18 @@ export class InteractiveShell {
|
|
|
691
803
|
}
|
|
692
804
|
refreshContextGauge() {
|
|
693
805
|
const tokens = getContextWindowTokens(this.sessionState.model);
|
|
694
|
-
|
|
695
|
-
|
|
806
|
+
const normalizedTokens = typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
|
|
807
|
+
this.activeContextWindowTokens = normalizedTokens;
|
|
808
|
+
if (normalizedTokens !== null) {
|
|
809
|
+
this.latestTokenUsage = {
|
|
810
|
+
used: this.latestTokenUsage.used,
|
|
811
|
+
limit: normalizedTokens,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
696
814
|
}
|
|
697
815
|
updateContextUsage(percentage) {
|
|
698
816
|
this.uiAdapter.updateContextUsage(percentage);
|
|
699
|
-
this.terminalInput.setContextUsage(percentage);
|
|
817
|
+
this.terminalInput.setContextUsage(percentage, CONTEXT_AUTOCOMPACT_PERCENT);
|
|
700
818
|
}
|
|
701
819
|
refreshControlBar() {
|
|
702
820
|
this.terminalInput.setModeToggles({
|
|
@@ -704,9 +822,9 @@ export class InteractiveShell {
|
|
|
704
822
|
autoContinueEnabled: this.autoContinueEnabled,
|
|
705
823
|
verificationHotkey: 'alt+v',
|
|
706
824
|
autoContinueHotkey: 'alt+c',
|
|
825
|
+
thinkingModeLabel: this.thinkingMode,
|
|
826
|
+
thinkingHotkey: '/thinking',
|
|
707
827
|
});
|
|
708
|
-
// Update persistent model info display
|
|
709
|
-
this.terminalInput.setModelInfo(this.describeModelDetail());
|
|
710
828
|
this.refreshStatusLine();
|
|
711
829
|
this.terminalInput.render();
|
|
712
830
|
}
|
|
@@ -737,6 +855,25 @@ export class InteractiveShell {
|
|
|
737
855
|
// Set main status (tool execution, etc.) - shown when not overridden
|
|
738
856
|
const statusText = this.formatStatusLine(this.statusLineState);
|
|
739
857
|
this.terminalInput.setStatusMessage(statusText);
|
|
858
|
+
// Surface meta header (elapsed + context usage) above the divider
|
|
859
|
+
const elapsedSeconds = this.statusLineState
|
|
860
|
+
? Math.max(0, Math.floor((Date.now() - this.statusLineState.startedAt) / 1000))
|
|
861
|
+
: null;
|
|
862
|
+
const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
|
|
863
|
+
const tokensUsed = this.latestTokenUsage.used;
|
|
864
|
+
const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
|
|
865
|
+
this.terminalInput.setMetaStatus({
|
|
866
|
+
elapsedSeconds,
|
|
867
|
+
tokensUsed,
|
|
868
|
+
tokenLimit,
|
|
869
|
+
thinkingMs,
|
|
870
|
+
thinkingHasContent: display.isSpinnerActive(),
|
|
871
|
+
});
|
|
872
|
+
// Keep model/provider visible in the controls bar
|
|
873
|
+
this.terminalInput.setModelContext({
|
|
874
|
+
model: this.sessionState.model,
|
|
875
|
+
provider: this.providerLabel(this.sessionState.provider),
|
|
876
|
+
});
|
|
740
877
|
if (forceRender) {
|
|
741
878
|
this.terminalInput.render();
|
|
742
879
|
}
|
|
@@ -796,15 +933,14 @@ export class InteractiveShell {
|
|
|
796
933
|
this.terminalInput.render();
|
|
797
934
|
}
|
|
798
935
|
/**
|
|
799
|
-
* Log
|
|
800
|
-
* This creates a persistent log entry that remains visible during and after streaming.
|
|
936
|
+
* Log user prompt to the scroll region so it's part of the conversation flow.
|
|
801
937
|
*/
|
|
802
938
|
logUserPrompt(text) {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
this.terminalInput.
|
|
939
|
+
if (!text.trim())
|
|
940
|
+
return;
|
|
941
|
+
// Format with user prompt prefix and write to scroll region
|
|
942
|
+
const formatted = `${theme.user('>')} ${text}\n`;
|
|
943
|
+
this.terminalInput.writeToScrollRegion(formatted);
|
|
808
944
|
}
|
|
809
945
|
requestPromptRefresh(force = false) {
|
|
810
946
|
if (force) {
|
|
@@ -829,16 +965,40 @@ export class InteractiveShell {
|
|
|
829
965
|
this.stopStreamingHeartbeat();
|
|
830
966
|
// Enter global streaming mode - blocks all non-streaming UI output
|
|
831
967
|
enterStreamingMode();
|
|
968
|
+
// Set up scroll region for streaming content
|
|
969
|
+
this.terminalInput.enterStreamingScrollRegion();
|
|
832
970
|
this.uiUpdates.setMode('streaming');
|
|
833
971
|
this.streamingHeartbeatStart = Date.now();
|
|
834
972
|
this.streamingHeartbeatFrame = 0;
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
973
|
+
const initialFrame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
|
|
974
|
+
this.streamingStatusLabel = this.buildStreamingStatus(`${initialFrame} ${label}`, 0);
|
|
975
|
+
display.updateStreamingStatus(this.streamingStatusLabel);
|
|
976
|
+
this.refreshStatusLine(true);
|
|
977
|
+
// Periodically refresh the pinned input/status region while streaming so
|
|
978
|
+
// elapsed time remains visible without interrupting the scroll region.
|
|
979
|
+
this.uiUpdates.startHeartbeat('streaming', {
|
|
980
|
+
intervalMs: 1000,
|
|
981
|
+
lane: 'heartbeat',
|
|
982
|
+
mode: ['streaming', 'processing'],
|
|
983
|
+
coalesceKey: 'streaming:heartbeat',
|
|
984
|
+
run: () => {
|
|
985
|
+
const elapsedSeconds = this.streamingHeartbeatStart
|
|
986
|
+
? Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000))
|
|
987
|
+
: 0;
|
|
988
|
+
this.streamingHeartbeatFrame =
|
|
989
|
+
(this.streamingHeartbeatFrame + 1) % STREAMING_SPINNER_FRAMES.length;
|
|
990
|
+
const frame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
|
|
991
|
+
this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${label}`, elapsedSeconds);
|
|
992
|
+
display.updateStreamingStatus(this.streamingStatusLabel);
|
|
993
|
+
this.refreshStatusLine(true);
|
|
994
|
+
},
|
|
995
|
+
});
|
|
838
996
|
}
|
|
839
997
|
stopStreamingHeartbeat() {
|
|
840
998
|
// Exit global streaming mode - allows UI to render again
|
|
841
999
|
exitStreamingMode();
|
|
1000
|
+
// Exit scroll region mode
|
|
1001
|
+
this.terminalInput.exitStreamingScrollRegion();
|
|
842
1002
|
this.uiUpdates.stopHeartbeat('streaming');
|
|
843
1003
|
this.streamingHeartbeatStart = null;
|
|
844
1004
|
this.streamingHeartbeatFrame = 0;
|
|
@@ -850,10 +1010,28 @@ export class InteractiveShell {
|
|
|
850
1010
|
// Force refresh to update the input area now that streaming has ended
|
|
851
1011
|
this.refreshStatusLine(true);
|
|
852
1012
|
}
|
|
853
|
-
buildStreamingStatus(label) {
|
|
1013
|
+
buildStreamingStatus(label, elapsedSeconds) {
|
|
854
1014
|
const detail = this.describeModelDetail();
|
|
855
|
-
const
|
|
856
|
-
|
|
1015
|
+
const elapsedLabel = typeof elapsedSeconds === 'number' && elapsedSeconds >= 0
|
|
1016
|
+
? theme.ui.muted(this.formatElapsedShort(elapsedSeconds))
|
|
1017
|
+
: null;
|
|
1018
|
+
const prefix = theme.info('⏺');
|
|
1019
|
+
const parts = [label];
|
|
1020
|
+
if (detail) {
|
|
1021
|
+
parts.push(theme.ui.muted('·'), detail);
|
|
1022
|
+
}
|
|
1023
|
+
if (elapsedLabel) {
|
|
1024
|
+
parts.push(theme.ui.muted('·'), elapsedLabel);
|
|
1025
|
+
}
|
|
1026
|
+
return `${prefix} ${parts.join(' ')}`.trim();
|
|
1027
|
+
}
|
|
1028
|
+
formatElapsedShort(seconds) {
|
|
1029
|
+
if (seconds < 60) {
|
|
1030
|
+
return `${seconds}s`;
|
|
1031
|
+
}
|
|
1032
|
+
const minutes = Math.floor(seconds / 60);
|
|
1033
|
+
const remaining = seconds % 60;
|
|
1034
|
+
return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`;
|
|
857
1035
|
}
|
|
858
1036
|
refreshQueueIndicators() {
|
|
859
1037
|
if (this.isProcessing) {
|
|
@@ -1101,17 +1279,6 @@ export class InteractiveShell {
|
|
|
1101
1279
|
case '/discover':
|
|
1102
1280
|
await this.discoverModelsCommand();
|
|
1103
1281
|
break;
|
|
1104
|
-
case '/memory':
|
|
1105
|
-
this.handleMemoryCommand(input);
|
|
1106
|
-
break;
|
|
1107
|
-
case '/clear':
|
|
1108
|
-
display.clear();
|
|
1109
|
-
this.cachedHistory = [];
|
|
1110
|
-
display.showInfo('Conversation cleared.');
|
|
1111
|
-
break;
|
|
1112
|
-
case '/help':
|
|
1113
|
-
this.showHelp();
|
|
1114
|
-
break;
|
|
1115
1282
|
default:
|
|
1116
1283
|
if (!(await this.tryCustomSlashCommand(command, input))) {
|
|
1117
1284
|
display.showWarning(`Unknown command "${command}".`);
|
|
@@ -1420,99 +1587,6 @@ export class InteractiveShell {
|
|
|
1420
1587
|
// Display keyboard shortcuts help (Claude Code style)
|
|
1421
1588
|
display.showSystemMessage(formatShortcutsHelp());
|
|
1422
1589
|
}
|
|
1423
|
-
handleMemoryCommand(input) {
|
|
1424
|
-
const tokens = input.trim().split(/\s+/).slice(1);
|
|
1425
|
-
const action = (tokens.shift() ?? 'show').toLowerCase();
|
|
1426
|
-
switch (action) {
|
|
1427
|
-
case '':
|
|
1428
|
-
case 'show':
|
|
1429
|
-
case 'list': {
|
|
1430
|
-
this.showMemoryStatus();
|
|
1431
|
-
break;
|
|
1432
|
-
}
|
|
1433
|
-
case 'paths': {
|
|
1434
|
-
this.showMemoryPaths();
|
|
1435
|
-
break;
|
|
1436
|
-
}
|
|
1437
|
-
case 'edit': {
|
|
1438
|
-
const level = (tokens[0] ?? 'project').toLowerCase();
|
|
1439
|
-
this.openMemoryForEdit(level);
|
|
1440
|
-
break;
|
|
1441
|
-
}
|
|
1442
|
-
default:
|
|
1443
|
-
display.showWarning('Usage: /memory [show|paths|edit <user|project>]');
|
|
1444
|
-
break;
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
showMemoryStatus() {
|
|
1448
|
-
const memory = loadMemory(this.workingDir);
|
|
1449
|
-
const lines = [];
|
|
1450
|
-
lines.push(theme.bold('Persistent Memory'));
|
|
1451
|
-
lines.push('');
|
|
1452
|
-
if (memory.sources.length === 0) {
|
|
1453
|
-
lines.push(theme.secondary('No memory files found.'));
|
|
1454
|
-
lines.push('');
|
|
1455
|
-
lines.push(`Create ${theme.info('EROSOLAR.md')} in your project to add persistent context.`);
|
|
1456
|
-
lines.push(`Use ${theme.info('/memory edit project')} to create one.`);
|
|
1457
|
-
}
|
|
1458
|
-
else {
|
|
1459
|
-
for (const source of memory.sources) {
|
|
1460
|
-
const levelLabel = source.level === 'enterprise' ? 'Enterprise' :
|
|
1461
|
-
source.level === 'user' ? 'User' : 'Project';
|
|
1462
|
-
const preview = source.content.slice(0, 200).replace(/\n/g, ' ').trim();
|
|
1463
|
-
const truncated = source.content.length > 200 ? '...' : '';
|
|
1464
|
-
lines.push(`${theme.success('●')} ${theme.bold(levelLabel)}: ${source.path}`);
|
|
1465
|
-
lines.push(` ${theme.dim(preview + truncated)}`);
|
|
1466
|
-
lines.push('');
|
|
1467
|
-
}
|
|
1468
|
-
if (memory.importedPaths.length > memory.sources.length) {
|
|
1469
|
-
lines.push(theme.secondary(`Imported ${memory.importedPaths.length - memory.sources.length} additional files via @imports.`));
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
display.showSystemMessage(lines.join('\n'));
|
|
1473
|
-
}
|
|
1474
|
-
showMemoryPaths() {
|
|
1475
|
-
const paths = listMemoryPaths(this.workingDir);
|
|
1476
|
-
const lines = [];
|
|
1477
|
-
lines.push(theme.bold('Memory File Locations'));
|
|
1478
|
-
lines.push('');
|
|
1479
|
-
for (const { level, path, exists } of paths) {
|
|
1480
|
-
const icon = exists ? theme.success('✓') : theme.dim('○');
|
|
1481
|
-
const levelLabel = level.charAt(0).toUpperCase() + level.slice(1);
|
|
1482
|
-
lines.push(`${icon} ${levelLabel}: ${path}`);
|
|
1483
|
-
}
|
|
1484
|
-
lines.push('');
|
|
1485
|
-
lines.push(theme.secondary('Create any of these files to add persistent memory.'));
|
|
1486
|
-
lines.push(theme.secondary('Use @path/to/file.md syntax to import other files.'));
|
|
1487
|
-
display.showSystemMessage(lines.join('\n'));
|
|
1488
|
-
}
|
|
1489
|
-
openMemoryForEdit(level) {
|
|
1490
|
-
let targetPath;
|
|
1491
|
-
if (level === 'user') {
|
|
1492
|
-
targetPath = getUserMemoryEditPath();
|
|
1493
|
-
}
|
|
1494
|
-
else if (level === 'project') {
|
|
1495
|
-
targetPath = getDefaultProjectMemoryPath(this.workingDir);
|
|
1496
|
-
}
|
|
1497
|
-
else {
|
|
1498
|
-
display.showWarning('Specify "user" or "project" to edit. Enterprise memory is read-only.');
|
|
1499
|
-
return;
|
|
1500
|
-
}
|
|
1501
|
-
display.showInfo(`Memory file: ${targetPath}`);
|
|
1502
|
-
display.showInfo('Create or edit this file to add persistent context for the AI.');
|
|
1503
|
-
display.showInfo('');
|
|
1504
|
-
display.showInfo('Example EROSOLAR.md content:');
|
|
1505
|
-
display.showInfo('');
|
|
1506
|
-
display.showInfo(theme.dim(`# Project Guidelines
|
|
1507
|
-
|
|
1508
|
-
When working in this codebase:
|
|
1509
|
-
- Follow TypeScript strict mode conventions
|
|
1510
|
-
- Use functional patterns where appropriate
|
|
1511
|
-
- Run tests before committing
|
|
1512
|
-
|
|
1513
|
-
@./docs/coding-standards.md
|
|
1514
|
-
`));
|
|
1515
|
-
}
|
|
1516
1590
|
showFileChangeSummary() {
|
|
1517
1591
|
const summary = this._fileChangeTracker.getSummary();
|
|
1518
1592
|
const changes = this._fileChangeTracker.getAllChanges();
|
|
@@ -1555,11 +1629,11 @@ When working in this codebase:
|
|
|
1555
1629
|
display.showSystemMessage(lines.join('\n'));
|
|
1556
1630
|
}
|
|
1557
1631
|
showAlphaZeroMetrics() {
|
|
1558
|
-
const summary = this.
|
|
1632
|
+
const summary = this.alphaZeroMetrics.getPerformanceSummary();
|
|
1559
1633
|
display.showSystemMessage(summary);
|
|
1560
1634
|
}
|
|
1561
1635
|
showImprovementSuggestions() {
|
|
1562
|
-
const suggestions = this.
|
|
1636
|
+
const suggestions = this.alphaZeroMetrics.getImprovementSuggestions();
|
|
1563
1637
|
if (suggestions.length === 0) {
|
|
1564
1638
|
display.showInfo('No improvement suggestions at this time. Keep using the shell to generate metrics!');
|
|
1565
1639
|
return;
|
|
@@ -1605,7 +1679,9 @@ When working in this codebase:
|
|
|
1605
1679
|
}
|
|
1606
1680
|
}
|
|
1607
1681
|
lines.push(theme.secondary('CLI Flags:'));
|
|
1682
|
+
lines.push(' --alpha-zero Enable Alpha Zero 2 RL framework');
|
|
1608
1683
|
lines.push(' --coding Enable enhanced coding tools');
|
|
1684
|
+
lines.push(' --security Enable security research tools');
|
|
1609
1685
|
lines.push(' --all-plugins Enable all optional plugins');
|
|
1610
1686
|
display.showSystemMessage(lines.join('\n'));
|
|
1611
1687
|
}
|
|
@@ -1654,6 +1730,7 @@ When working in this codebase:
|
|
|
1654
1730
|
model: this.sessionState.model,
|
|
1655
1731
|
workspaceRoot: this.workingDir,
|
|
1656
1732
|
messages: history,
|
|
1733
|
+
scrollbackBuffer: this.terminalInput.getScrollbackBuffer(),
|
|
1657
1734
|
});
|
|
1658
1735
|
this.cachedHistory = history;
|
|
1659
1736
|
this.updateActiveSession(summary, true);
|
|
@@ -1832,6 +1909,7 @@ When working in this codebase:
|
|
|
1832
1909
|
workspaceRoot: this.workingDir,
|
|
1833
1910
|
title: this.activeSessionTitle,
|
|
1834
1911
|
messages: this.cachedHistory,
|
|
1912
|
+
scrollbackBuffer: this.terminalInput.getScrollbackBuffer(),
|
|
1835
1913
|
});
|
|
1836
1914
|
}
|
|
1837
1915
|
describeWorkspaceOptions() {
|
|
@@ -1849,6 +1927,75 @@ When working in this codebase:
|
|
|
1849
1927
|
}
|
|
1850
1928
|
return `${warning.label}: ${warning.reason}.`;
|
|
1851
1929
|
}
|
|
1930
|
+
buildLaunchCommandPalette() {
|
|
1931
|
+
const entries = [];
|
|
1932
|
+
const secretsSummary = this.summarizeSecretsForPalette();
|
|
1933
|
+
const toolSummary = this.getToolSelectionSummary();
|
|
1934
|
+
const autosaveLabel = this.autosaveEnabled ? 'on' : 'off';
|
|
1935
|
+
for (const command of this.slashCommands) {
|
|
1936
|
+
const entry = {
|
|
1937
|
+
command: command.command,
|
|
1938
|
+
description: command.description,
|
|
1939
|
+
category: command.category ?? 'other',
|
|
1940
|
+
};
|
|
1941
|
+
switch (command.command) {
|
|
1942
|
+
case '/secrets':
|
|
1943
|
+
if (secretsSummary.text) {
|
|
1944
|
+
entry.description = `${command.description} (${secretsSummary.text})`;
|
|
1945
|
+
entry.tone = secretsSummary.tone;
|
|
1946
|
+
}
|
|
1947
|
+
break;
|
|
1948
|
+
case '/tools':
|
|
1949
|
+
if (toolSummary) {
|
|
1950
|
+
entry.description = `${command.description} (${toolSummary})`;
|
|
1951
|
+
}
|
|
1952
|
+
break;
|
|
1953
|
+
case '/sessions':
|
|
1954
|
+
entry.description = `${command.description} (autosave ${autosaveLabel})`;
|
|
1955
|
+
break;
|
|
1956
|
+
case '/model':
|
|
1957
|
+
entry.description = `${command.description} (current: ${this.sessionState.model})`;
|
|
1958
|
+
break;
|
|
1959
|
+
case '/provider':
|
|
1960
|
+
entry.description = `${command.description} (current: ${this.providerLabel(this.sessionState.provider)})`;
|
|
1961
|
+
break;
|
|
1962
|
+
default:
|
|
1963
|
+
break;
|
|
1964
|
+
}
|
|
1965
|
+
entries.push(entry);
|
|
1966
|
+
}
|
|
1967
|
+
return entries;
|
|
1968
|
+
}
|
|
1969
|
+
summarizeSecretsForPalette() {
|
|
1970
|
+
const definitions = listSecretDefinitions();
|
|
1971
|
+
if (!definitions.length) {
|
|
1972
|
+
return { text: null };
|
|
1973
|
+
}
|
|
1974
|
+
const missing = definitions.filter((definition) => !getSecretValue(definition.id));
|
|
1975
|
+
if (missing.length === 0) {
|
|
1976
|
+
return { text: 'all configured', tone: 'success' };
|
|
1977
|
+
}
|
|
1978
|
+
const labels = missing.map((definition) => definition.label ?? definition.id);
|
|
1979
|
+
return { text: `missing ${this.formatList(labels)}`, tone: 'warn' };
|
|
1980
|
+
}
|
|
1981
|
+
getToolSelectionSummary() {
|
|
1982
|
+
const toolSettings = loadToolSettings();
|
|
1983
|
+
const selection = buildEnabledToolSet(toolSettings);
|
|
1984
|
+
const options = getToolToggleOptions();
|
|
1985
|
+
if (!options.length) {
|
|
1986
|
+
return null;
|
|
1987
|
+
}
|
|
1988
|
+
const enabledCount = options.filter((option) => selection.has(option.id)).length;
|
|
1989
|
+
return `${enabledCount}/${options.length} enabled`;
|
|
1990
|
+
}
|
|
1991
|
+
formatList(values, maxItems = 3) {
|
|
1992
|
+
if (!values.length) {
|
|
1993
|
+
return '';
|
|
1994
|
+
}
|
|
1995
|
+
const shown = values.slice(0, maxItems);
|
|
1996
|
+
const suffix = values.length > maxItems ? ', …' : '';
|
|
1997
|
+
return `${shown.join(', ')}${suffix}`;
|
|
1998
|
+
}
|
|
1852
1999
|
buildSlashCommandList(header) {
|
|
1853
2000
|
const lines = [theme.gradient.primary(header), ''];
|
|
1854
2001
|
for (const command of this.slashCommands) {
|
|
@@ -2317,7 +2464,7 @@ When working in this codebase:
|
|
|
2317
2464
|
this.autosaveIfEnabled();
|
|
2318
2465
|
// Track metrics with Alpha Zero 2
|
|
2319
2466
|
const elapsedMs = Date.now() - requestStartTime;
|
|
2320
|
-
this.
|
|
2467
|
+
this.alphaZeroMetrics.recordMessage(elapsedMs);
|
|
2321
2468
|
if (!responseText?.trim()) {
|
|
2322
2469
|
display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
|
|
2323
2470
|
}
|
|
@@ -2335,14 +2482,10 @@ When working in this codebase:
|
|
|
2335
2482
|
this.stopStreamingHeartbeat();
|
|
2336
2483
|
this.isProcessing = false;
|
|
2337
2484
|
this.terminalInput.setStreaming(false);
|
|
2338
|
-
this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
|
|
2339
2485
|
this.uiAdapter.endProcessing('Ready for prompts');
|
|
2340
2486
|
this.setIdleStatus();
|
|
2341
2487
|
display.newLine();
|
|
2342
2488
|
this.updateStatusMessage(null);
|
|
2343
|
-
// Claude Code style: Show unified status bar before prompt
|
|
2344
|
-
// This creates consistent UI between startup and post-streaming
|
|
2345
|
-
this.showUnifiedStatusBar();
|
|
2346
2489
|
queueMicrotask(() => this.uiUpdates.setMode('idle'));
|
|
2347
2490
|
// CRITICAL: Ensure readline prompt is active for user input
|
|
2348
2491
|
// Claude Code style: New prompt naturally appears at bottom
|
|
@@ -2419,13 +2562,14 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
|
|
|
2419
2562
|
try {
|
|
2420
2563
|
// Send the request and capture the response (streaming disabled)
|
|
2421
2564
|
display.showThinking('Responding...');
|
|
2565
|
+
this.refreshStatusLine(true);
|
|
2422
2566
|
const response = await agent.send(currentPrompt, true);
|
|
2423
2567
|
await this.awaitPendingCleanup();
|
|
2424
2568
|
this.captureHistorySnapshot();
|
|
2425
2569
|
this.autosaveIfEnabled();
|
|
2426
2570
|
// Track metrics
|
|
2427
2571
|
const elapsedMs = Date.now() - overallStartTime;
|
|
2428
|
-
this.
|
|
2572
|
+
this.alphaZeroMetrics.recordMessage(elapsedMs);
|
|
2429
2573
|
if (!response?.trim()) {
|
|
2430
2574
|
display.showWarning('Model returned an empty response. Retrying this iteration...');
|
|
2431
2575
|
consecutiveNoProgress++;
|
|
@@ -2562,7 +2706,6 @@ What's the next action?`;
|
|
|
2562
2706
|
this.stopStreamingHeartbeat();
|
|
2563
2707
|
this.isProcessing = false;
|
|
2564
2708
|
this.terminalInput.setStreaming(false);
|
|
2565
|
-
this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
|
|
2566
2709
|
this.uiAdapter.endProcessing('Ready for prompts');
|
|
2567
2710
|
this.setIdleStatus();
|
|
2568
2711
|
this.updateStatusMessage(null);
|
|
@@ -2919,8 +3062,10 @@ What's the next action?`;
|
|
|
2919
3062
|
try {
|
|
2920
3063
|
// Send the error to the agent for fixing
|
|
2921
3064
|
display.showThinking('Analyzing build errors');
|
|
3065
|
+
this.refreshStatusLine(true);
|
|
2922
3066
|
const response = await this.agent.send(prompt, true);
|
|
2923
3067
|
display.stopThinking();
|
|
3068
|
+
this.refreshStatusLine(true);
|
|
2924
3069
|
if (response) {
|
|
2925
3070
|
display.showAssistantMessage(response, { isFinal: true });
|
|
2926
3071
|
}
|
|
@@ -2947,8 +3092,8 @@ What's the next action?`;
|
|
|
2947
3092
|
};
|
|
2948
3093
|
this.agent = this.runtimeSession.createAgent(selection, {
|
|
2949
3094
|
onStreamChunk: (chunk) => {
|
|
2950
|
-
// Stream output
|
|
2951
|
-
|
|
3095
|
+
// Stream output using clean streamContent() - chat box floats below
|
|
3096
|
+
this.terminalInput.streamContent(chunk);
|
|
2952
3097
|
},
|
|
2953
3098
|
onStreamFallback: (info) => this.handleStreamingFallback(info),
|
|
2954
3099
|
onAssistantMessage: (content, metadata) => {
|
|
@@ -2970,18 +3115,16 @@ What's the next action?`;
|
|
|
2970
3115
|
display.showAssistantMessage(finalContent, enriched);
|
|
2971
3116
|
}
|
|
2972
3117
|
}
|
|
2973
|
-
//
|
|
3118
|
+
// Status shown in mode controls bar - no separate status line needed
|
|
2974
3119
|
display.stopThinking();
|
|
2975
|
-
//
|
|
2976
|
-
let contextInfo;
|
|
3120
|
+
// Update context usage for mode controls display
|
|
2977
3121
|
if (enriched.contextWindowTokens && metadata.usage) {
|
|
2978
3122
|
const total = this.totalTokens(metadata.usage);
|
|
2979
3123
|
if (total && total > 0) {
|
|
2980
3124
|
const percentage = Math.round((total / enriched.contextWindowTokens) * 100);
|
|
2981
|
-
|
|
3125
|
+
this.updateContextUsage(percentage);
|
|
2982
3126
|
}
|
|
2983
3127
|
}
|
|
2984
|
-
display.showStatusLine('Ready for prompts', enriched.elapsedMs, contextInfo);
|
|
2985
3128
|
// Auto-verify changes: build first (catches type errors), then tests
|
|
2986
3129
|
void this.enforceAutoBuild('final-response');
|
|
2987
3130
|
void this.enforceAutoTests('final-response');
|
|
@@ -3051,7 +3194,6 @@ What's the next action?`;
|
|
|
3051
3194
|
this.stopStreamingHeartbeat();
|
|
3052
3195
|
this.updateStatusMessage(null);
|
|
3053
3196
|
this.terminalInput.setStreaming(false);
|
|
3054
|
-
this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
|
|
3055
3197
|
this.terminalInput.render();
|
|
3056
3198
|
},
|
|
3057
3199
|
onVerificationNeeded: () => {
|
|
@@ -3088,7 +3230,6 @@ What's the next action?`;
|
|
|
3088
3230
|
resetChatBoxAfterModelSwap() {
|
|
3089
3231
|
this.updateStatusMessage(null);
|
|
3090
3232
|
this.terminalInput.setStreaming(false);
|
|
3091
|
-
this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
|
|
3092
3233
|
this.terminalInput.render();
|
|
3093
3234
|
this.ensureReadlineReady();
|
|
3094
3235
|
}
|
|
@@ -3153,9 +3294,14 @@ What's the next action?`;
|
|
|
3153
3294
|
return null;
|
|
3154
3295
|
}
|
|
3155
3296
|
const usageRatio = total / windowTokens;
|
|
3297
|
+
this.latestTokenUsage = {
|
|
3298
|
+
used: total,
|
|
3299
|
+
limit: windowTokens,
|
|
3300
|
+
};
|
|
3156
3301
|
// Always update context usage in the UI
|
|
3157
3302
|
const percentUsed = Math.round(usageRatio * 100);
|
|
3158
3303
|
this.updateContextUsage(percentUsed);
|
|
3304
|
+
this.refreshStatusLine(true);
|
|
3159
3305
|
if (usageRatio < CONTEXT_USAGE_THRESHOLD) {
|
|
3160
3306
|
return null;
|
|
3161
3307
|
}
|
|
@@ -3422,6 +3568,113 @@ What's the next action?`;
|
|
|
3422
3568
|
this.sessionState.reasoningEffort = preset.reasoningEffort;
|
|
3423
3569
|
}
|
|
3424
3570
|
}
|
|
3571
|
+
/**
|
|
3572
|
+
* Build the session banner with comprehensive feature status.
|
|
3573
|
+
*/
|
|
3574
|
+
buildBanner() {
|
|
3575
|
+
const terminalWidth = output.columns ?? 100;
|
|
3576
|
+
const width = Math.min(terminalWidth - 4, 110);
|
|
3577
|
+
// Collect tool categories for display
|
|
3578
|
+
const toolCategories = this.collectToolCategories();
|
|
3579
|
+
return renderSessionFrame({
|
|
3580
|
+
profileLabel: this.profileLabel,
|
|
3581
|
+
profileName: this.profile,
|
|
3582
|
+
model: this.sessionState.model,
|
|
3583
|
+
provider: this.sessionState.provider,
|
|
3584
|
+
workspace: this.workingDir,
|
|
3585
|
+
version: this.version,
|
|
3586
|
+
width,
|
|
3587
|
+
features: {
|
|
3588
|
+
verification: this.verificationEnabled,
|
|
3589
|
+
autoContinue: this.autoContinueEnabled,
|
|
3590
|
+
thinkingMode: this.thinkingMode,
|
|
3591
|
+
plugins: this._enabledPlugins,
|
|
3592
|
+
tools: toolCategories,
|
|
3593
|
+
sessionId: this.activeSessionId ?? undefined,
|
|
3594
|
+
},
|
|
3595
|
+
});
|
|
3596
|
+
}
|
|
3597
|
+
/**
|
|
3598
|
+
* Collect tool categories for banner display.
|
|
3599
|
+
*/
|
|
3600
|
+
collectToolCategories() {
|
|
3601
|
+
const categories = [];
|
|
3602
|
+
try {
|
|
3603
|
+
const providerTools = this.runtimeSession.toolRuntime.listProviderTools();
|
|
3604
|
+
if (providerTools.length > 0) {
|
|
3605
|
+
// Group by category (first word of tool name or namespace)
|
|
3606
|
+
const groups = new Map();
|
|
3607
|
+
for (const tool of providerTools) {
|
|
3608
|
+
const category = this.extractToolCategory(tool.name);
|
|
3609
|
+
groups.set(category, (groups.get(category) || 0) + 1);
|
|
3610
|
+
}
|
|
3611
|
+
// Convert to array sorted by count
|
|
3612
|
+
const sorted = Array.from(groups.entries())
|
|
3613
|
+
.sort((a, b) => b[1] - a[1])
|
|
3614
|
+
.slice(0, 5); // Top 5 categories
|
|
3615
|
+
for (const [name, count] of sorted) {
|
|
3616
|
+
categories.push({ name, count, icon: this.getToolCategoryIcon(name) });
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
catch {
|
|
3621
|
+
// Ignore errors in tool collection
|
|
3622
|
+
}
|
|
3623
|
+
return categories;
|
|
3624
|
+
}
|
|
3625
|
+
/**
|
|
3626
|
+
* Extract category from tool name.
|
|
3627
|
+
*/
|
|
3628
|
+
extractToolCategory(toolName) {
|
|
3629
|
+
// Common tool prefixes
|
|
3630
|
+
const prefixMap = {
|
|
3631
|
+
git: 'Git',
|
|
3632
|
+
npm: 'NPM',
|
|
3633
|
+
bash: 'Shell',
|
|
3634
|
+
file: 'Files',
|
|
3635
|
+
read: 'Files',
|
|
3636
|
+
write: 'Files',
|
|
3637
|
+
edit: 'Files',
|
|
3638
|
+
search: 'Search',
|
|
3639
|
+
glob: 'Search',
|
|
3640
|
+
grep: 'Search',
|
|
3641
|
+
web: 'Web',
|
|
3642
|
+
fetch: 'Web',
|
|
3643
|
+
test: 'Testing',
|
|
3644
|
+
build: 'Build',
|
|
3645
|
+
deploy: 'Deploy',
|
|
3646
|
+
cloud: 'Cloud',
|
|
3647
|
+
browser: 'Browser',
|
|
3648
|
+
};
|
|
3649
|
+
const lower = toolName.toLowerCase();
|
|
3650
|
+
for (const [prefix, category] of Object.entries(prefixMap)) {
|
|
3651
|
+
if (lower.startsWith(prefix)) {
|
|
3652
|
+
return category;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
// Default to first word capitalized
|
|
3656
|
+
const firstWord = toolName.split(/[_\-\s]/)[0] || 'Other';
|
|
3657
|
+
return firstWord.charAt(0).toUpperCase() + firstWord.slice(1).toLowerCase();
|
|
3658
|
+
}
|
|
3659
|
+
/**
|
|
3660
|
+
* Get icon for tool category.
|
|
3661
|
+
*/
|
|
3662
|
+
getToolCategoryIcon(category) {
|
|
3663
|
+
const icons = {
|
|
3664
|
+
Git: '⎇',
|
|
3665
|
+
NPM: '📦',
|
|
3666
|
+
Shell: '⌘',
|
|
3667
|
+
Files: '📁',
|
|
3668
|
+
Search: '🔍',
|
|
3669
|
+
Web: '🌐',
|
|
3670
|
+
Testing: '🧪',
|
|
3671
|
+
Build: '🔧',
|
|
3672
|
+
Deploy: '🚀',
|
|
3673
|
+
Cloud: '☁',
|
|
3674
|
+
Browser: '🌐',
|
|
3675
|
+
};
|
|
3676
|
+
return icons[category] || '⚙';
|
|
3677
|
+
}
|
|
3425
3678
|
refreshBannerSessionInfo() {
|
|
3426
3679
|
const nextState = {
|
|
3427
3680
|
model: this.sessionState.model,
|
|
@@ -3432,13 +3685,11 @@ What's the next action?`;
|
|
|
3432
3685
|
return;
|
|
3433
3686
|
}
|
|
3434
3687
|
this.refreshContextGauge();
|
|
3435
|
-
display
|
|
3436
|
-
//
|
|
3437
|
-
this.terminalInput.setModelInfo(this.describeModelDetail());
|
|
3688
|
+
// Banner is no longer stored in display - it was streamed as content
|
|
3689
|
+
// Model/provider changes are visible in the control bar
|
|
3438
3690
|
if (!this.isProcessing) {
|
|
3439
3691
|
this.setIdleStatus();
|
|
3440
3692
|
}
|
|
3441
|
-
// Pinned header rows are disabled; scroll region handles all output.
|
|
3442
3693
|
this.bannerSessionState = nextState;
|
|
3443
3694
|
}
|
|
3444
3695
|
providerLabel(id) {
|