erosolar-cli 2.1.171 → 2.1.172
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 +1 -1
- package/agents/erosolar-code.rules.json +2 -2
- package/agents/general.rules.json +3 -21
- package/dist/StringUtils.d.ts +8 -0
- package/dist/StringUtils.d.ts.map +1 -0
- package/dist/StringUtils.js +11 -0
- package/dist/StringUtils.js.map +1 -0
- package/dist/capabilities/statusCapability.js +2 -2
- package/dist/capabilities/statusCapability.js.map +1 -1
- package/dist/contracts/agent-schemas.json +5 -5
- package/dist/core/agent.d.ts +24 -83
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +248 -499
- package/dist/core/agent.js.map +1 -1
- package/dist/core/aiFlowSupervisor.d.ts +44 -0
- package/dist/core/aiFlowSupervisor.d.ts.map +1 -0
- package/dist/core/aiFlowSupervisor.js +299 -0
- package/dist/core/aiFlowSupervisor.js.map +1 -0
- package/dist/core/cliTestHarness.d.ts +200 -0
- package/dist/core/cliTestHarness.d.ts.map +1 -0
- package/dist/core/cliTestHarness.js +549 -0
- package/dist/core/cliTestHarness.js.map +1 -0
- package/dist/core/preferences.d.ts +0 -1
- package/dist/core/preferences.d.ts.map +1 -1
- package/dist/core/preferences.js +1 -8
- package/dist/core/preferences.js.map +1 -1
- package/dist/core/schemaValidator.js +3 -3
- package/dist/core/schemaValidator.js.map +1 -1
- package/dist/core/testUtils.d.ts +121 -0
- package/dist/core/testUtils.d.ts.map +1 -0
- package/dist/core/testUtils.js +235 -0
- package/dist/core/testUtils.js.map +1 -0
- package/dist/core/toolPreconditions.d.ts +11 -0
- package/dist/core/toolPreconditions.d.ts.map +1 -1
- package/dist/core/toolPreconditions.js +164 -33
- package/dist/core/toolPreconditions.js.map +1 -1
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +114 -9
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/core/toolValidation.d.ts +116 -0
- package/dist/core/toolValidation.d.ts.map +1 -0
- package/dist/core/toolValidation.js +282 -0
- package/dist/core/toolValidation.js.map +1 -0
- package/dist/core/updateChecker.d.ts +1 -61
- package/dist/core/updateChecker.d.ts.map +1 -1
- package/dist/core/updateChecker.js +3 -147
- package/dist/core/updateChecker.js.map +1 -1
- package/dist/headless/evalMode.d.ts.map +1 -1
- package/dist/headless/evalMode.js +0 -6
- package/dist/headless/evalMode.js.map +1 -1
- package/dist/headless/headlessApp.d.ts.map +1 -1
- package/dist/headless/headlessApp.js +39 -6
- package/dist/headless/headlessApp.js.map +1 -1
- package/dist/mcp/sseClient.d.ts +1 -4
- package/dist/mcp/sseClient.d.ts.map +1 -1
- package/dist/mcp/sseClient.js +2 -36
- package/dist/mcp/sseClient.js.map +1 -1
- package/dist/mcp/stdioClient.d.ts +1 -4
- package/dist/mcp/stdioClient.d.ts.map +1 -1
- package/dist/mcp/stdioClient.js +1 -41
- package/dist/mcp/stdioClient.js.map +1 -1
- package/dist/mcp/toolBridge.d.ts +0 -3
- package/dist/mcp/toolBridge.d.ts.map +1 -1
- package/dist/mcp/toolBridge.js +2 -2
- package/dist/mcp/toolBridge.js.map +1 -1
- package/dist/mcp/types.d.ts +0 -18
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
- package/dist/plugins/tools/nodeDefaults.js +2 -0
- package/dist/plugins/tools/nodeDefaults.js.map +1 -1
- package/dist/providers/openaiResponsesProvider.d.ts.map +1 -1
- package/dist/providers/openaiResponsesProvider.js +74 -79
- package/dist/providers/openaiResponsesProvider.js.map +1 -1
- package/dist/runtime/agentController.d.ts.map +1 -1
- package/dist/runtime/agentController.js +3 -6
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/runtime/agentSession.d.ts +2 -0
- 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 +18 -20
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +291 -329
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.d.ts.map +1 -1
- package/dist/shell/shellApp.js +8 -16
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/shell/systemPrompt.d.ts.map +1 -1
- package/dist/shell/systemPrompt.js +15 -4
- package/dist/shell/systemPrompt.js.map +1 -1
- package/dist/subagents/taskRunner.js +1 -2
- package/dist/subagents/taskRunner.js.map +1 -1
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +8 -101
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/diffUtils.d.ts +2 -8
- package/dist/tools/diffUtils.d.ts.map +1 -1
- package/dist/tools/diffUtils.js +13 -72
- package/dist/tools/diffUtils.js.map +1 -1
- package/dist/tools/grepTools.d.ts.map +1 -1
- package/dist/tools/grepTools.js +2 -10
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/planningTools.d.ts +10 -0
- package/dist/tools/planningTools.d.ts.map +1 -1
- package/dist/tools/planningTools.js +16 -0
- package/dist/tools/planningTools.js.map +1 -1
- package/dist/tools/searchTools.d.ts.map +1 -1
- package/dist/tools/searchTools.js +2 -4
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/ui/PromptController.d.ts +4 -4
- package/dist/ui/PromptController.d.ts.map +1 -1
- package/dist/ui/PromptController.js +7 -1
- package/dist/ui/PromptController.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +28 -292
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +121 -1513
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +30 -136
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +370 -955
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/animatedStatus.d.ts +6 -128
- package/dist/ui/animatedStatus.d.ts.map +1 -1
- package/dist/ui/animatedStatus.js +50 -383
- package/dist/ui/animatedStatus.js.map +1 -1
- package/dist/ui/display.d.ts +26 -182
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +97 -678
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/layout.d.ts +1 -0
- package/dist/ui/layout.d.ts.map +1 -1
- package/dist/ui/layout.js +12 -0
- package/dist/ui/layout.js.map +1 -1
- package/dist/ui/orchestration/UIUpdateCoordinator.d.ts +7 -61
- package/dist/ui/orchestration/UIUpdateCoordinator.d.ts.map +1 -1
- package/dist/ui/orchestration/UIUpdateCoordinator.js +20 -232
- package/dist/ui/orchestration/UIUpdateCoordinator.js.map +1 -1
- package/dist/ui/planOverlay.d.ts +28 -0
- package/dist/ui/planOverlay.d.ts.map +1 -0
- package/dist/ui/planOverlay.js +156 -0
- package/dist/ui/planOverlay.js.map +1 -0
- package/dist/ui/shortcutsHelp.d.ts.map +1 -1
- package/dist/ui/shortcutsHelp.js +1 -0
- package/dist/ui/shortcutsHelp.js.map +1 -1
- package/dist/ui/streamingFormatter.d.ts +30 -0
- package/dist/ui/streamingFormatter.d.ts.map +1 -0
- package/dist/ui/streamingFormatter.js +91 -0
- package/dist/ui/streamingFormatter.js.map +1 -0
- package/dist/ui/unified/index.d.ts +1 -30
- package/dist/ui/unified/index.d.ts.map +1 -1
- package/dist/ui/unified/index.js +2 -45
- package/dist/ui/unified/index.js.map +1 -1
- package/dist/utils/errorUtils.d.ts +16 -0
- package/dist/utils/errorUtils.d.ts.map +1 -0
- package/dist/utils/errorUtils.js +66 -0
- package/dist/utils/errorUtils.js.map +1 -0
- package/package.json +2 -1
- package/dist/codex/capabilities/codexCoreCapability.d.ts +0 -6
- package/dist/codex/capabilities/codexCoreCapability.d.ts.map +0 -1
- package/dist/codex/capabilities/codexCoreCapability.js +0 -516
- package/dist/codex/capabilities/codexCoreCapability.js.map +0 -1
- package/dist/codex/fs.d.ts +0 -4
- package/dist/codex/fs.d.ts.map +0 -1
- package/dist/codex/fs.js +0 -25
- package/dist/codex/fs.js.map +0 -1
- package/dist/codex/persistence/planStore.d.ts +0 -4
- package/dist/codex/persistence/planStore.d.ts.map +0 -1
- package/dist/codex/persistence/planStore.js +0 -59
- package/dist/codex/persistence/planStore.js.map +0 -1
- package/dist/codex/pluginAllowlist.d.ts +0 -4
- package/dist/codex/pluginAllowlist.d.ts.map +0 -1
- package/dist/codex/pluginAllowlist.js +0 -14
- package/dist/codex/pluginAllowlist.js.map +0 -1
- package/dist/codex/types.d.ts +0 -21
- package/dist/codex/types.d.ts.map +0 -1
- package/dist/codex/types.js +0 -62
- package/dist/codex/types.js.map +0 -1
- package/dist/core/reliabilityPrompt.d.ts +0 -9
- package/dist/core/reliabilityPrompt.d.ts.map +0 -1
- package/dist/core/reliabilityPrompt.js +0 -31
- package/dist/core/reliabilityPrompt.js.map +0 -1
- package/dist/ui/UnifiedUIController.d.ts +0 -81
- package/dist/ui/UnifiedUIController.d.ts.map +0 -1
- package/dist/ui/UnifiedUIController.js +0 -212
- package/dist/ui/UnifiedUIController.js.map +0 -1
- package/dist/ui/animation/AnimationScheduler.d.ts +0 -192
- package/dist/ui/animation/AnimationScheduler.d.ts.map +0 -1
- package/dist/ui/animation/AnimationScheduler.js +0 -432
- package/dist/ui/animation/AnimationScheduler.js.map +0 -1
- package/dist/ui/inPlaceUpdater.d.ts +0 -181
- package/dist/ui/inPlaceUpdater.d.ts.map +0 -1
- package/dist/ui/inPlaceUpdater.js +0 -515
- package/dist/ui/inPlaceUpdater.js.map +0 -1
- package/dist/ui/interrupts/InterruptManager.d.ts +0 -142
- package/dist/ui/interrupts/InterruptManager.d.ts.map +0 -1
- package/dist/ui/interrupts/InterruptManager.js +0 -439
- package/dist/ui/interrupts/InterruptManager.js.map +0 -1
- package/dist/ui/telemetry/ResponseTracker.d.ts +0 -22
- package/dist/ui/telemetry/ResponseTracker.d.ts.map +0 -1
- package/dist/ui/telemetry/ResponseTracker.js +0 -60
- package/dist/ui/telemetry/ResponseTracker.js.map +0 -1
- package/dist/ui/telemetry/UITelemetry.d.ts +0 -181
- package/dist/ui/telemetry/UITelemetry.d.ts.map +0 -1
- package/dist/ui/telemetry/UITelemetry.js +0 -446
- package/dist/ui/telemetry/UITelemetry.js.map +0 -1
- package/dist/ui/unified/layout.d.ts +0 -12
- package/dist/ui/unified/layout.d.ts.map +0 -1
- package/dist/ui/unified/layout.js +0 -96
- package/dist/ui/unified/layout.js.map +0 -1
|
@@ -7,6 +7,7 @@ import { join, resolve } from 'node:path';
|
|
|
7
7
|
import { display } from '../ui/display.js';
|
|
8
8
|
import { theme } from '../ui/theme.js';
|
|
9
9
|
import { getTerminalColumns } from '../ui/layout.js';
|
|
10
|
+
import { StreamingResponseFormatter } from '../ui/streamingFormatter.js';
|
|
10
11
|
import { getContextWindowTokens } from '../core/contextWindow.js';
|
|
11
12
|
import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
|
|
12
13
|
import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
|
|
@@ -18,6 +19,7 @@ import { detectNetworkError } from '../core/errors/networkErrors.js';
|
|
|
18
19
|
import { buildWorkspaceContext } from '../workspace.js';
|
|
19
20
|
import { buildInteractiveSystemPrompt } from './systemPrompt.js';
|
|
20
21
|
import { getTaskCompletionDetector, resetTaskCompletionDetector, } from './taskCompletionDetector.js';
|
|
22
|
+
import { assessAiFlow } from '../core/aiFlowSupervisor.js';
|
|
21
23
|
import { discoverAllModels, quickCheckProviders, getCachedDiscoveredModels, sortModelsByPriority } from '../core/modelDiscovery.js';
|
|
22
24
|
import { getModels, getSlashCommands, getProviders } from '../core/agentSchemaLoader.js';
|
|
23
25
|
import { loadMcpServers } from '../mcp/config.js';
|
|
@@ -27,7 +29,7 @@ import { SkillRepository } from '../skills/skillRepository.js';
|
|
|
27
29
|
import { createSkillTools } from '../tools/skillTools.js';
|
|
28
30
|
import { FileChangeTracker } from './fileChangeTracker.js';
|
|
29
31
|
import { formatShortcutsHelp } from '../ui/shortcutsHelp.js';
|
|
30
|
-
import { setPlanApprovalCallback } from '../tools/planningTools.js';
|
|
32
|
+
import { setPlanApprovalCallback, setPlanUpdateCallback } from '../tools/planningTools.js';
|
|
31
33
|
import { MetricsTracker } from '../core/metricsTracker.js';
|
|
32
34
|
import { detectFailure, clearActionHistory, findRecoveryStrategy, } from '../core/failureRecovery.js';
|
|
33
35
|
import { addToolPattern } from '../core/learningPersistence.js';
|
|
@@ -40,6 +42,7 @@ import { startOffsecRun, resumeOffsecRun, recordOffsecOutcome, getOffsecNextActi
|
|
|
40
42
|
import { generateTestFlows, detectBugs, detectUIUpdates, saveTestFlows, saveBugReports, saveUIUpdates, getTestFlowStatus, } from '../core/intelligentTestFlows.js';
|
|
41
43
|
import { PromptController } from '../ui/PromptController.js';
|
|
42
44
|
import { enterStreamingMode, exitStreamingMode, isStreamingMode } from '../ui/globalWriteLock.js';
|
|
45
|
+
import { PlanOverlay } from '../ui/planOverlay.js';
|
|
43
46
|
import { setGlobalAIEnhancer } from '../tools/localExplore.js';
|
|
44
47
|
import { createProvider } from '../providers/providerFactory.js';
|
|
45
48
|
import { getParallelAgentManager } from '../subagents/parallelAgentManager.js';
|
|
@@ -90,7 +93,6 @@ export class InteractiveShell {
|
|
|
90
93
|
profile;
|
|
91
94
|
profileLabel;
|
|
92
95
|
workingDir;
|
|
93
|
-
codexContext;
|
|
94
96
|
runtimeSession;
|
|
95
97
|
baseSystemPrompt;
|
|
96
98
|
workspaceOptions;
|
|
@@ -130,14 +132,16 @@ export class InteractiveShell {
|
|
|
130
132
|
activeContextWindowTokens = null;
|
|
131
133
|
latestTokenUsage = { used: null, limit: null };
|
|
132
134
|
planApprovalBridgeRegistered = false;
|
|
135
|
+
planUpdateBridgeRegistered = false;
|
|
136
|
+
planOverlay = new PlanOverlay();
|
|
133
137
|
contextCompactionInFlight = false;
|
|
134
138
|
contextCompactionLog = [];
|
|
135
139
|
lastContextWarningLevel = null;
|
|
136
140
|
sessionPreferences;
|
|
137
141
|
autosaveEnabled;
|
|
142
|
+
autoContinueEnabled;
|
|
138
143
|
verificationEnabled = false;
|
|
139
144
|
criticalApprovalMode = 'auto';
|
|
140
|
-
codexApprovalForced = false;
|
|
141
145
|
editGuardMode = 'display-edits';
|
|
142
146
|
pendingPermissionInput = null;
|
|
143
147
|
suppressNextNetworkReset = false;
|
|
@@ -181,6 +185,7 @@ export class InteractiveShell {
|
|
|
181
185
|
streamingOutputSuppressed = false;
|
|
182
186
|
aiRuntimeStart = null;
|
|
183
187
|
aiRuntimeTotalMs = 0;
|
|
188
|
+
streamingFormatter = null;
|
|
184
189
|
streamingThoughtBuffer = '';
|
|
185
190
|
statusLineState = null;
|
|
186
191
|
statusMessageOverride = null;
|
|
@@ -192,7 +197,6 @@ export class InteractiveShell {
|
|
|
192
197
|
version;
|
|
193
198
|
alternateScreenEnabled;
|
|
194
199
|
welcomeShown = false;
|
|
195
|
-
guardrailBannerShown = false;
|
|
196
200
|
renderer;
|
|
197
201
|
// Message queue for streaming mode coordination - prevents race conditions
|
|
198
202
|
pendingMessages = [];
|
|
@@ -200,18 +204,14 @@ export class InteractiveShell {
|
|
|
200
204
|
this.profile = config.profile;
|
|
201
205
|
this.profileLabel = config.profileLabel;
|
|
202
206
|
this.workingDir = config.workingDir;
|
|
203
|
-
this.codexContext = config.codexContext;
|
|
204
207
|
this.runtimeSession = config.session;
|
|
205
208
|
this.baseSystemPrompt = config.baseSystemPrompt;
|
|
206
209
|
this.workspaceOptions = { ...config.workspaceOptions };
|
|
207
210
|
this.sessionPreferences = loadSessionPreferences();
|
|
208
211
|
this.thinkingMode = this.sessionPreferences.thinkingMode;
|
|
209
212
|
this.autosaveEnabled = this.sessionPreferences.autosave;
|
|
213
|
+
this.autoContinueEnabled = this.sessionPreferences.autoContinue;
|
|
210
214
|
this.criticalApprovalMode = this.sessionPreferences.criticalApprovalMode;
|
|
211
|
-
if (this.codexContext && this.shouldForceApprovalMode(this.codexContext.approvalPolicy)) {
|
|
212
|
-
this.criticalApprovalMode = 'approval';
|
|
213
|
-
this.codexApprovalForced = true;
|
|
214
|
-
}
|
|
215
215
|
const featureFlags = loadFeatureFlags();
|
|
216
216
|
this.verificationEnabled = featureFlags.verification === true;
|
|
217
217
|
this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
|
|
@@ -269,6 +269,11 @@ export class InteractiveShell {
|
|
|
269
269
|
description: 'Switch between auto and approval mode for high-impact actions',
|
|
270
270
|
category: 'configuration',
|
|
271
271
|
});
|
|
272
|
+
this.slashCommands.push({
|
|
273
|
+
command: '/plan',
|
|
274
|
+
description: 'Show, hide, or clear the current plan panel (/plan show|hide|clear)',
|
|
275
|
+
category: 'workflow',
|
|
276
|
+
});
|
|
272
277
|
this.slashCommands.push({
|
|
273
278
|
command: '/offsec',
|
|
274
279
|
description: 'AlphaZero offensive security run (start/status/next)',
|
|
@@ -287,10 +292,6 @@ export class InteractiveShell {
|
|
|
287
292
|
this.uiAdapter.setToolStatusCallback((status) => {
|
|
288
293
|
this.updateStatusMessage(status ?? null);
|
|
289
294
|
});
|
|
290
|
-
// Set up activity callback to update activity line with streaming bash output
|
|
291
|
-
this.uiAdapter.setActivityCallback((activity) => {
|
|
292
|
-
this.renderer?.setActivity(activity);
|
|
293
|
-
});
|
|
294
295
|
this.skillRepository = new SkillRepository({
|
|
295
296
|
workingDir: this.workingDir,
|
|
296
297
|
env: process.env,
|
|
@@ -304,18 +305,20 @@ export class InteractiveShell {
|
|
|
304
305
|
onQueue: (text) => this.handleQueuedInput(text),
|
|
305
306
|
onCtrlC: ({ hadBuffer }) => this.handleCtrlCPress(hadBuffer),
|
|
306
307
|
onInterrupt: () => this.handleInterrupt(),
|
|
307
|
-
onExit: () => this.
|
|
308
|
+
onExit: () => this.shutdown(),
|
|
309
|
+
onExpandToolResult: () => display.expandLastToolResult?.(),
|
|
308
310
|
onChange: ({ text, cursor }) => this.handleInputChange(text, cursor, 'renderer'),
|
|
309
311
|
onEditModeChange: (mode) => this.handleEditModeChange(mode),
|
|
310
312
|
onToggleVerify: () => this.toggleVerificationMode(),
|
|
313
|
+
onToggleAutoContinue: () => this.toggleAutoContinueMode(),
|
|
311
314
|
onToggleThinking: () => this.cycleThinkingMode(),
|
|
312
315
|
onClearContext: () => this.handleClearContext(),
|
|
313
316
|
onToggleCriticalApproval: () => this.toggleCriticalApprovalMode('shortcut'),
|
|
314
|
-
onExpandToolResult: () => this.expandLastToolResult(),
|
|
315
317
|
});
|
|
316
318
|
// Share renderer with Display so all output flows through the unified queue
|
|
317
319
|
this.renderer = this.terminalInput.getRenderer();
|
|
318
320
|
display.setRenderer(this.renderer);
|
|
321
|
+
this.uiAdapter.attachRenderer(this.renderer);
|
|
319
322
|
display.setInlinePanelHandler((content) => {
|
|
320
323
|
if (!this.shouldCaptureInlinePanel()) {
|
|
321
324
|
return false;
|
|
@@ -335,6 +338,7 @@ export class InteractiveShell {
|
|
|
335
338
|
// The control bar will be refreshed after the welcome banner
|
|
336
339
|
// Allow planning tools (e.g., ProposePlan) to open the interactive approval UI just like Codex CLI
|
|
337
340
|
this.registerPlanApprovalBridge();
|
|
341
|
+
this.registerPlanUpdateBridge();
|
|
338
342
|
// Set up command autocomplete with all slash commands
|
|
339
343
|
this.setupCommandAutocomplete();
|
|
340
344
|
this.rebuildAgent();
|
|
@@ -455,21 +459,18 @@ export class InteractiveShell {
|
|
|
455
459
|
const top = `╭${'─'.repeat(labelLeft)}${label}${'─'.repeat(labelRight)}╮`;
|
|
456
460
|
const bottom = `╰${'─'.repeat(contentWidth)}╯`;
|
|
457
461
|
const userName = process.env['USER'] || 'there';
|
|
458
|
-
const logo = '⟣╍◎╍⟢';
|
|
462
|
+
const logo = clamp('⟣╍◎╍⟢');
|
|
459
463
|
const versionLabel = version ? clamp(`${brand} · v${version}`) : brand;
|
|
460
464
|
const modelLine = clamp(model);
|
|
461
465
|
const workspaceLine = clamp(workspace);
|
|
462
|
-
|
|
463
|
-
const welcomeLine = clamp(`${logo} Welcome back ${userName}! ${logo}`);
|
|
464
|
-
// Quick reference hints
|
|
465
|
-
const hintsLine = clamp('/model · /secrets · /help · ? shortcuts');
|
|
466
|
+
const welcomeLine = clamp(`Welcome back ${userName}!`);
|
|
466
467
|
const lines = [
|
|
467
468
|
frame(top),
|
|
468
469
|
frame(`│${padCenter(welcomeLine, contentWidth)}│`),
|
|
470
|
+
frame(`│${padCenter(logo, contentWidth)}│`),
|
|
469
471
|
frame(`│${padCenter(modelLine, contentWidth)}│`),
|
|
470
472
|
frame(`│${padCenter(versionLabel, contentWidth)}│`),
|
|
471
473
|
frame(`│${padCenter(workspaceLine, contentWidth)}│`),
|
|
472
|
-
frame(`│${padCenter(hintsLine, contentWidth)}│`),
|
|
473
474
|
frame(bottom),
|
|
474
475
|
].join('\n');
|
|
475
476
|
if (this.renderer) {
|
|
@@ -480,6 +481,8 @@ export class InteractiveShell {
|
|
|
480
481
|
else {
|
|
481
482
|
display.stream(`\n${lines}\n`);
|
|
482
483
|
}
|
|
484
|
+
// Check for updates asynchronously (non-blocking)
|
|
485
|
+
void this.checkAndShowUpdates();
|
|
483
486
|
// Keep UI pinned; no scrollback banners
|
|
484
487
|
this.requestPromptRefresh(true);
|
|
485
488
|
}
|
|
@@ -515,79 +518,24 @@ export class InteractiveShell {
|
|
|
515
518
|
}
|
|
516
519
|
return pathValue;
|
|
517
520
|
}
|
|
518
|
-
shouldForceApprovalMode(policy) {
|
|
519
|
-
return policy === 'on-request' || policy === 'untrusted' || policy === 'never';
|
|
520
|
-
}
|
|
521
|
-
getGuardrailMeta() {
|
|
522
|
-
if (!this.codexContext) {
|
|
523
|
-
return {};
|
|
524
|
-
}
|
|
525
|
-
const approvals = this.codexContext.approvalPolicy === 'on-request' || this.codexContext.approvalPolicy === 'untrusted'
|
|
526
|
-
? 'ask'
|
|
527
|
-
: this.codexContext.approvalPolicy === 'never'
|
|
528
|
-
? 'never'
|
|
529
|
-
: 'auto';
|
|
530
|
-
const sandbox = this.codexContext.sandboxMode === 'workspace-write'
|
|
531
|
-
? 'workspace'
|
|
532
|
-
: this.codexContext.sandboxMode === 'danger-full-access'
|
|
533
|
-
? 'danger'
|
|
534
|
-
: this.codexContext.sandboxMode;
|
|
535
|
-
const network = this.codexContext.networkAccess === 'enabled' ? 'enabled' : 'restricted';
|
|
536
|
-
return { sandbox, network, approvals };
|
|
537
|
-
}
|
|
538
|
-
describePlanPath() {
|
|
539
|
-
const planPath = join(this.workingDir, '.erosolar', 'codex', 'plan.json');
|
|
540
|
-
const relative = planPath.startsWith(this.workingDir) && planPath.length > this.workingDir.length
|
|
541
|
-
? planPath.slice(this.workingDir.length + 1)
|
|
542
|
-
: planPath;
|
|
543
|
-
return this.abbreviatePath(relative);
|
|
544
|
-
}
|
|
545
|
-
maybeShowCodexGuardrails() {
|
|
546
|
-
if (!this.codexContext || this.guardrailBannerShown) {
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
const meta = this.getGuardrailMeta();
|
|
550
|
-
const lines = [];
|
|
551
|
-
lines.push(`Guardrails: sandbox ${meta.sandbox ?? 'workspace'} · network ${meta.network ?? 'restricted'} · approvals ${meta.approvals ?? 'auto'}`);
|
|
552
|
-
lines.push('Flow: plan before edits, targeted reads, one in-progress step, validate once at the end.');
|
|
553
|
-
const planPath = this.describePlanPath();
|
|
554
|
-
if (planPath) {
|
|
555
|
-
lines.push(`Plan file: ${planPath}`);
|
|
556
|
-
}
|
|
557
|
-
if (this.codexApprovalForced) {
|
|
558
|
-
lines.push('Approval mode set to ask per environment policy.');
|
|
559
|
-
}
|
|
560
|
-
this.streamEventBlock(lines.join('\n'));
|
|
561
|
-
this.guardrailBannerShown = true;
|
|
562
|
-
}
|
|
563
521
|
async checkAndShowUpdates() {
|
|
564
522
|
try {
|
|
565
|
-
const { checkForUpdates,
|
|
523
|
+
const { checkForUpdates, formatUpdateNotification, maybeAutoUpdate } = await import('../core/updateChecker.js');
|
|
566
524
|
const currentVersion = this.version || this.getPackageInfo().version || '1.7.458';
|
|
567
525
|
const updateInfo = await checkForUpdates(currentVersion);
|
|
568
526
|
if (!updateInfo || !updateInfo.updateAvailable) {
|
|
569
527
|
return;
|
|
570
528
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
// Get user's preference-based decision (no interactive prompt)
|
|
577
|
-
const sessionPrefs = loadSessionPreferences();
|
|
578
|
-
const decision = getUpdateDecision(sessionPrefs.autoUpdate);
|
|
579
|
-
if (decision === 'skip') {
|
|
580
|
-
// User chose to always skip - silently ignore updates
|
|
529
|
+
const updateResult = await maybeAutoUpdate(currentVersion, {
|
|
530
|
+
updateInfo,
|
|
531
|
+
logger: (message) => this.streamEventBlock(message),
|
|
532
|
+
});
|
|
533
|
+
if (updateResult?.updated) {
|
|
581
534
|
return;
|
|
582
535
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// User chose to always update - perform background update
|
|
587
|
-
await performBackgroundUpdate(updateInfo, (message) => this.streamEventBlock(message));
|
|
588
|
-
}
|
|
589
|
-
// If decision === 'ask', we just show the banner with instructions
|
|
590
|
-
// User can run /update check or /update auto to update
|
|
536
|
+
const note = this.describeUpdateSkipReason(updateResult);
|
|
537
|
+
const notification = formatUpdateNotification(updateInfo, note);
|
|
538
|
+
this.streamEventBlock(notification);
|
|
591
539
|
}
|
|
592
540
|
catch {
|
|
593
541
|
// Silently fail - don't interrupt user experience
|
|
@@ -671,20 +619,16 @@ export class InteractiveShell {
|
|
|
671
619
|
this.pushUiEvent('raw', block);
|
|
672
620
|
}
|
|
673
621
|
async start(initialPrompt) {
|
|
674
|
-
// Check for updates BEFORE terminal input starts (to avoid keypress conflicts)
|
|
675
|
-
await this.checkAndShowUpdates();
|
|
676
622
|
// Initialize the renderer before emitting the banner so we don't render the prompt twice
|
|
677
623
|
this.terminalInput.start();
|
|
678
624
|
this.resetRendererStreamingMode();
|
|
679
625
|
await this.showWelcomeBanner();
|
|
680
|
-
this.maybeShowCodexGuardrails();
|
|
681
626
|
// Now refresh control bar with profile/model/version info
|
|
682
627
|
this.refreshControlBar();
|
|
683
628
|
// Now sync renderer and control bar state
|
|
684
629
|
this.syncRendererInput();
|
|
685
|
-
//
|
|
686
|
-
|
|
687
|
-
this.renderer?.render();
|
|
630
|
+
// Ensure the prompt/control bar is rendered after the welcome banner
|
|
631
|
+
this.ensureReadlineReady();
|
|
688
632
|
if (initialPrompt) {
|
|
689
633
|
await this.enqueuePromptForProcessing(initialPrompt, 'programmatic');
|
|
690
634
|
return;
|
|
@@ -692,7 +636,7 @@ export class InteractiveShell {
|
|
|
692
636
|
this.showLaunchCommandPalette();
|
|
693
637
|
// Ensure the terminal input is visible
|
|
694
638
|
this.syncRendererInput();
|
|
695
|
-
this.
|
|
639
|
+
this.ensureReadlineReady();
|
|
696
640
|
}
|
|
697
641
|
showLaunchCommandPalette() {
|
|
698
642
|
// Disabled: Quick commands palette takes up too much space
|
|
@@ -760,10 +704,6 @@ export class InteractiveShell {
|
|
|
760
704
|
*/
|
|
761
705
|
async runPromptJob(job) {
|
|
762
706
|
try {
|
|
763
|
-
// Show thinking indicator instead of "processing queued prompt"
|
|
764
|
-
display.showThinking('Thinking…');
|
|
765
|
-
// Re-echo the prompt to make it clear what's being processed
|
|
766
|
-
this.renderer?.emitPrompt(job.text);
|
|
767
707
|
await this.processInputBlock(job.text);
|
|
768
708
|
job.resolve();
|
|
769
709
|
}
|
|
@@ -859,6 +799,7 @@ export class InteractiveShell {
|
|
|
859
799
|
'/output-style',
|
|
860
800
|
// Mode toggles
|
|
861
801
|
'/thinking',
|
|
802
|
+
'/autocontinue',
|
|
862
803
|
// Discovery and plugins
|
|
863
804
|
'/local', '/discover',
|
|
864
805
|
'/plugins',
|
|
@@ -948,22 +889,25 @@ export class InteractiveShell {
|
|
|
948
889
|
: '🛡️ High-impact actions now require your approval. (Ctrl+Shift+A to toggle; use /approvals ask to persist)';
|
|
949
890
|
display.showSystemMessage(message);
|
|
950
891
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
892
|
+
toggleAutoContinueMode() {
|
|
893
|
+
this.setAutoContinueMode(!this.autoContinueEnabled, 'shortcut');
|
|
894
|
+
}
|
|
895
|
+
setAutoContinueMode(enabled, source) {
|
|
896
|
+
const changed = this.autoContinueEnabled !== enabled;
|
|
897
|
+
this.autoContinueEnabled = enabled;
|
|
898
|
+
saveSessionPreferences({ autoContinue: this.autoContinueEnabled });
|
|
899
|
+
if (this.agent) {
|
|
900
|
+
this.agent.setAutoContinue(this.autoContinueEnabled);
|
|
901
|
+
}
|
|
902
|
+
this.refreshControlBar();
|
|
903
|
+
if (!changed && source === 'shortcut') {
|
|
959
904
|
return;
|
|
960
905
|
}
|
|
961
|
-
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
display.stream(`\n${header}\n${content}\n${footer}\n\n`);
|
|
906
|
+
const modeLabel = this.autoContinueEnabled ? 'enabled' : 'disabled';
|
|
907
|
+
const behavior = this.autoContinueEnabled
|
|
908
|
+
? 'The model will be auto-prompted to continue when it expresses intent but does not use tools.'
|
|
909
|
+
: 'The model will not be auto-prompted to continue.';
|
|
910
|
+
display.showInfo(`Auto-continue ${modeLabel}. ${behavior} Toggle with Ctrl+Shift+C.`);
|
|
967
911
|
}
|
|
968
912
|
/**
|
|
969
913
|
* Cycle through thinking modes (Tab shortcut).
|
|
@@ -1117,13 +1061,6 @@ export class InteractiveShell {
|
|
|
1117
1061
|
}
|
|
1118
1062
|
this.ctrlCHandledThisPress = false;
|
|
1119
1063
|
}
|
|
1120
|
-
/**
|
|
1121
|
-
* Handle exit request (Ctrl+C with empty buffer in idle mode)
|
|
1122
|
-
*/
|
|
1123
|
-
handleExit() {
|
|
1124
|
-
display.showSystemMessage('\nGoodbye!\n');
|
|
1125
|
-
this.shutdown();
|
|
1126
|
-
}
|
|
1127
1064
|
/**
|
|
1128
1065
|
* Gracefully tear down the shell and exit
|
|
1129
1066
|
*/
|
|
@@ -1133,7 +1070,7 @@ export class InteractiveShell {
|
|
|
1133
1070
|
}
|
|
1134
1071
|
this.shuttingDown = true;
|
|
1135
1072
|
// Stop any active spinner to prevent process hang
|
|
1136
|
-
display.stopThinking(
|
|
1073
|
+
display.stopThinking();
|
|
1137
1074
|
this.stopStreamingHeartbeat('quit', { quiet: true });
|
|
1138
1075
|
this.endAiRuntime();
|
|
1139
1076
|
this.uiUpdates.dispose();
|
|
@@ -1145,6 +1082,7 @@ export class InteractiveShell {
|
|
|
1145
1082
|
this.pendingCleanup = null;
|
|
1146
1083
|
// Unregister plan approval bridge
|
|
1147
1084
|
setPlanApprovalCallback(null);
|
|
1085
|
+
setPlanUpdateCallback(null);
|
|
1148
1086
|
// Dispose terminal input handler
|
|
1149
1087
|
this.terminalInput.dispose();
|
|
1150
1088
|
// Dispose unified UI adapter
|
|
@@ -1165,6 +1103,38 @@ export class InteractiveShell {
|
|
|
1165
1103
|
});
|
|
1166
1104
|
this.planApprovalBridgeRegistered = true;
|
|
1167
1105
|
}
|
|
1106
|
+
registerPlanUpdateBridge() {
|
|
1107
|
+
if (this.planUpdateBridgeRegistered) {
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
setPlanUpdateCallback((payload) => {
|
|
1111
|
+
if (!payload || !payload.steps.length) {
|
|
1112
|
+
this.planOverlay.clear();
|
|
1113
|
+
this.applyPlanOverlay();
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
this.applyPlanUpdate(payload.steps, payload.explanation);
|
|
1117
|
+
});
|
|
1118
|
+
this.planUpdateBridgeRegistered = true;
|
|
1119
|
+
}
|
|
1120
|
+
applyPlanUpdate(steps, explanation) {
|
|
1121
|
+
this.planOverlay.setPlan(steps, explanation);
|
|
1122
|
+
this.applyPlanOverlay();
|
|
1123
|
+
}
|
|
1124
|
+
applyPlanOverlay() {
|
|
1125
|
+
const lines = this.planOverlay.render();
|
|
1126
|
+
if (!this.renderer) {
|
|
1127
|
+
if (this.planOverlay.hasPlan()) {
|
|
1128
|
+
display.showSystemMessage(this.planOverlay.asText());
|
|
1129
|
+
}
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (!lines.length) {
|
|
1133
|
+
this.renderer.clearPersistentPanel();
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
this.renderer.setPersistentPanel(lines);
|
|
1137
|
+
}
|
|
1168
1138
|
/**
|
|
1169
1139
|
* Update status bar message
|
|
1170
1140
|
*/
|
|
@@ -1693,16 +1663,16 @@ export class InteractiveShell {
|
|
|
1693
1663
|
refreshControlBar() {
|
|
1694
1664
|
this.terminalInput.setModeToggles({
|
|
1695
1665
|
verificationEnabled: this.verificationEnabled,
|
|
1666
|
+
autoContinueEnabled: this.autoContinueEnabled,
|
|
1696
1667
|
verificationHotkey: 'ctrl+shift+v',
|
|
1668
|
+
autoContinueHotkey: 'ctrl+shift+c',
|
|
1697
1669
|
thinkingModeLabel: (this.thinkingMode || 'off').toString(),
|
|
1698
1670
|
thinkingHotkey: 'tab',
|
|
1699
1671
|
criticalApprovalMode: this.criticalApprovalMode,
|
|
1700
1672
|
criticalApprovalHotkey: 'ctrl+shift+a',
|
|
1701
1673
|
});
|
|
1702
1674
|
const workspaceDisplay = this.abbreviatePath(this.workingDir);
|
|
1703
|
-
const guardrails = this.getGuardrailMeta();
|
|
1704
1675
|
this.terminalInput.setChromeMeta({
|
|
1705
|
-
...guardrails,
|
|
1706
1676
|
profile: this.profileLabel,
|
|
1707
1677
|
workspace: workspaceDisplay,
|
|
1708
1678
|
directory: workspaceDisplay,
|
|
@@ -1786,14 +1756,15 @@ export class InteractiveShell {
|
|
|
1786
1756
|
*/
|
|
1787
1757
|
refreshStatusLine(forceRender = false) {
|
|
1788
1758
|
const elapsedSeconds = this.getAiRuntimeSeconds();
|
|
1759
|
+
const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
|
|
1789
1760
|
const tokensUsed = this.latestTokenUsage.used;
|
|
1790
1761
|
const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
|
|
1791
1762
|
this.terminalInput.setMetaStatus({
|
|
1792
1763
|
elapsedSeconds,
|
|
1793
1764
|
tokensUsed,
|
|
1794
1765
|
tokenLimit,
|
|
1795
|
-
thinkingMs
|
|
1796
|
-
thinkingHasContent:
|
|
1766
|
+
thinkingMs,
|
|
1767
|
+
thinkingHasContent: display.isSpinnerActive(),
|
|
1797
1768
|
});
|
|
1798
1769
|
// Keep model/provider visible in the controls bar
|
|
1799
1770
|
this.terminalInput.setModelContext({
|
|
@@ -1969,16 +1940,12 @@ export class InteractiveShell {
|
|
|
1969
1940
|
enterStreamingMode();
|
|
1970
1941
|
// Set up scroll region for streaming content
|
|
1971
1942
|
this.uiUpdates.setMode('streaming');
|
|
1972
|
-
// Show activity status with animated spinner - use provided label or default
|
|
1973
|
-
const activityLabel = label || 'Thinking';
|
|
1974
|
-
this.renderer?.setActivity(activityLabel);
|
|
1975
1943
|
this.streamingHeartbeatStart = Date.now();
|
|
1976
1944
|
this.streamingContentSeen = false;
|
|
1977
1945
|
this.streamingStatusText = null;
|
|
1978
1946
|
this.streamingStatusLastUpdate = null;
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
this.streamingOutputSuppressed = false;
|
|
1947
|
+
// Suppress raw streaming lines; show only a single summary/status + final response
|
|
1948
|
+
this.streamingOutputSuppressed = true;
|
|
1982
1949
|
const initialLabel = this.isMeaningfulStreamingSnippet(label)
|
|
1983
1950
|
? this.truncateStreamingLabel(label)
|
|
1984
1951
|
: this.getStreamingFallbackLabel();
|
|
@@ -2022,8 +1989,6 @@ export class InteractiveShell {
|
|
|
2022
1989
|
this.streamingStatusLastUpdate = null;
|
|
2023
1990
|
this.streamingStatusText = null;
|
|
2024
1991
|
this.streamingOutputSuppressed = false;
|
|
2025
|
-
// Clear activity status when streaming ends
|
|
2026
|
-
this.renderer?.setActivity(null);
|
|
2027
1992
|
// Emit a streaming note for stop/quit so the status stays inside the stream
|
|
2028
1993
|
if (reason === 'stop' || reason === 'quit') {
|
|
2029
1994
|
const note = reason === 'quit' ? 'Session closed.' : 'Stream stopped.';
|
|
@@ -2040,15 +2005,11 @@ export class InteractiveShell {
|
|
|
2040
2005
|
// Buffer for accumulating partial <thinking> tags during streaming
|
|
2041
2006
|
thinkingTagBuffer = '';
|
|
2042
2007
|
insideThinkingBlock = false;
|
|
2043
|
-
streamingTokenCount = 0;
|
|
2044
2008
|
handleStreamChunk(chunk, type = 'content') {
|
|
2045
2009
|
if (!chunk) {
|
|
2046
2010
|
return;
|
|
2047
2011
|
}
|
|
2048
2012
|
const isReasoning = type === 'reasoning';
|
|
2049
|
-
// Approximate token count (roughly 4 chars per token)
|
|
2050
|
-
this.streamingTokenCount += Math.ceil(chunk.length / 4);
|
|
2051
|
-
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
2052
2013
|
// Keep pinned status updated for all streaming chunks
|
|
2053
2014
|
this.updateStreamingStatusFromChunk(chunk);
|
|
2054
2015
|
// Handle <thinking> tags as separate events in the queue
|
|
@@ -2057,32 +2018,37 @@ export class InteractiveShell {
|
|
|
2057
2018
|
if (!processed.contentChunk && !processed.thinkingChunk) {
|
|
2058
2019
|
return;
|
|
2059
2020
|
}
|
|
2060
|
-
//
|
|
2061
|
-
|
|
2062
|
-
|
|
2021
|
+
// Emit thinking block as separate 'thought' event
|
|
2022
|
+
if (processed.thinkingChunk) {
|
|
2023
|
+
this.pushUiEvent('thought', `${theme.ui.muted('⏺')} ${theme.ui.muted('<thinking>')}${processed.thinkingChunk}${theme.ui.muted('</thinking>')}`);
|
|
2024
|
+
}
|
|
2063
2025
|
// Process regular content (skip if no content after extracting thinking)
|
|
2064
2026
|
const contentChunk = processed.contentChunk;
|
|
2065
2027
|
if (!contentChunk) {
|
|
2066
2028
|
return;
|
|
2067
2029
|
}
|
|
2068
2030
|
// Suppress raw streaming output in scrollback; keep only status + final message.
|
|
2069
|
-
// Reasoning
|
|
2070
|
-
if (this.streamingOutputSuppressed) {
|
|
2031
|
+
// Reasoning chunks bypass this so the thought process stays visible.
|
|
2032
|
+
if (this.streamingOutputSuppressed && !isReasoning) {
|
|
2071
2033
|
this.captureStreamingThought(contentChunk);
|
|
2072
2034
|
this.streamingContentSeen = true;
|
|
2073
|
-
// Update thinking indicator with a snippet of the content
|
|
2074
|
-
const prefix = isReasoning ? '○ ' : '';
|
|
2075
|
-
const snippet = contentChunk.replace(/\s+/g, ' ').trim().slice(0, 60);
|
|
2076
|
-
if (snippet) {
|
|
2077
|
-
display.updateThinking(prefix + snippet + (contentChunk.length > 60 ? '…' : ''));
|
|
2078
|
-
}
|
|
2079
2035
|
return;
|
|
2080
2036
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2037
|
+
if (!this.streamingFormatter) {
|
|
2038
|
+
this.streamingFormatter = new StreamingResponseFormatter(output.columns ?? undefined);
|
|
2039
|
+
this.pushUiEvent('streaming', this.streamingFormatter.header());
|
|
2040
|
+
}
|
|
2041
|
+
this.streamingFormatter.setMode(isReasoning ? 'reasoning' : 'content');
|
|
2042
|
+
const formatted = isReasoning
|
|
2043
|
+
? this.streamingFormatter.formatReasoningChunk(contentChunk)
|
|
2044
|
+
: this.streamingFormatter.formatChunk(contentChunk);
|
|
2083
2045
|
this.captureStreamingThought(contentChunk);
|
|
2084
|
-
|
|
2085
|
-
|
|
2046
|
+
if (formatted) {
|
|
2047
|
+
if (formatted.trim().length > 0) {
|
|
2048
|
+
this.streamingContentSeen = true;
|
|
2049
|
+
}
|
|
2050
|
+
this.pushUiEvent('streaming', formatted);
|
|
2051
|
+
}
|
|
2086
2052
|
}
|
|
2087
2053
|
/**
|
|
2088
2054
|
* Process streaming content to extract <thinking> blocks as separate events.
|
|
@@ -2130,12 +2096,22 @@ export class InteractiveShell {
|
|
|
2130
2096
|
}
|
|
2131
2097
|
return { thinkingChunk: thinkingContent, contentChunk: remainingContent };
|
|
2132
2098
|
}
|
|
2133
|
-
finishStreamingFormatter(
|
|
2134
|
-
|
|
2099
|
+
finishStreamingFormatter(note, options) {
|
|
2100
|
+
if (!this.streamingFormatter) {
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
const closing = this.streamingFormatter.finish({
|
|
2104
|
+
note,
|
|
2105
|
+
mode: options?.mode ?? 'complete',
|
|
2106
|
+
});
|
|
2107
|
+
if (closing) {
|
|
2108
|
+
this.pushUiEvent('streaming', closing);
|
|
2109
|
+
}
|
|
2135
2110
|
if (this.streamingThoughtBuffer.trim()) {
|
|
2136
2111
|
this.ui.controller.recordAssistantThought(this.streamingThoughtBuffer.trim());
|
|
2137
2112
|
}
|
|
2138
2113
|
this.streamingThoughtBuffer = '';
|
|
2114
|
+
this.streamingFormatter = null;
|
|
2139
2115
|
if (options?.refreshPrompt ?? true) {
|
|
2140
2116
|
this.requestPromptRefresh(true);
|
|
2141
2117
|
}
|
|
@@ -2427,6 +2403,9 @@ export class InteractiveShell {
|
|
|
2427
2403
|
case '/approvals':
|
|
2428
2404
|
this.handleApprovalsCommand(input);
|
|
2429
2405
|
break;
|
|
2406
|
+
case '/plan':
|
|
2407
|
+
this.handlePlanCommand(input);
|
|
2408
|
+
break;
|
|
2430
2409
|
case '/learn':
|
|
2431
2410
|
this.showLearningStatus(input);
|
|
2432
2411
|
break;
|
|
@@ -2474,6 +2453,9 @@ export class InteractiveShell {
|
|
|
2474
2453
|
case '/thinking':
|
|
2475
2454
|
this.handleThinkingCommand(input);
|
|
2476
2455
|
break;
|
|
2456
|
+
case '/autocontinue':
|
|
2457
|
+
this.handleAutoContinueCommand(input);
|
|
2458
|
+
break;
|
|
2477
2459
|
case '/shortcuts':
|
|
2478
2460
|
case '/keys':
|
|
2479
2461
|
this.handleShortcutsCommand();
|
|
@@ -2577,9 +2559,6 @@ export class InteractiveShell {
|
|
|
2577
2559
|
case '/permissions':
|
|
2578
2560
|
this.handlePermissionsCommand();
|
|
2579
2561
|
break;
|
|
2580
|
-
case '/update':
|
|
2581
|
-
await this.handleUpdateCommand(input);
|
|
2582
|
-
break;
|
|
2583
2562
|
case '/init':
|
|
2584
2563
|
this.handleInitCommand(input);
|
|
2585
2564
|
break;
|
|
@@ -2637,6 +2616,7 @@ export class InteractiveShell {
|
|
|
2637
2616
|
theme.bold(' Mode Toggles'),
|
|
2638
2617
|
` ${theme.info('Shift+Tab')} ${theme.ui.muted('Toggle edit mode (auto/ask)')}`,
|
|
2639
2618
|
` ${theme.info('Option+V')} ${theme.ui.muted('Toggle verification')}`,
|
|
2619
|
+
` ${theme.info('Option+C')} ${theme.ui.muted('Toggle auto-continue')}`,
|
|
2640
2620
|
` ${theme.info('Ctrl+Shift+A')} ${theme.ui.muted('Toggle approvals for high-impact actions')}`,
|
|
2641
2621
|
` ${theme.info('Option+T')} ${theme.ui.muted('Cycle thinking mode')}`,
|
|
2642
2622
|
` ${theme.info('Option+E')} ${theme.ui.muted('Toggle edit permission mode')}`,
|
|
@@ -4350,6 +4330,20 @@ export class InteractiveShell {
|
|
|
4350
4330
|
clearAutosaveSnapshot(this.profile);
|
|
4351
4331
|
display.showInfo('Cleared autosave history.');
|
|
4352
4332
|
}
|
|
4333
|
+
handleAutoContinueCommand(input) {
|
|
4334
|
+
const tokens = input.split(/\s+/).slice(1);
|
|
4335
|
+
const value = tokens[0]?.toLowerCase();
|
|
4336
|
+
if (!value) {
|
|
4337
|
+
// Show current status
|
|
4338
|
+
display.showInfo(`Auto-continue is ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. Use /autocontinue on|off or Ctrl+Shift+C to toggle.`);
|
|
4339
|
+
return;
|
|
4340
|
+
}
|
|
4341
|
+
if (value !== 'on' && value !== 'off') {
|
|
4342
|
+
display.showWarning('Usage: /autocontinue on|off');
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4345
|
+
this.setAutoContinueMode(value === 'on', 'command');
|
|
4346
|
+
}
|
|
4353
4347
|
// ==================== Erosolar-CLI Style Commands ====================
|
|
4354
4348
|
async handleRewindCommand(_input) {
|
|
4355
4349
|
const lines = [];
|
|
@@ -4581,6 +4575,7 @@ export class InteractiveShell {
|
|
|
4581
4575
|
lines.push(`${theme.primary('Provider/Model')}: ${theme.info(`${this.providerLabel(this.sessionState.provider)} · ${this.sessionState.model}`)}`);
|
|
4582
4576
|
lines.push(`${theme.primary('Workspace')}: ${theme.ui.muted(this.abbreviatePath(this.workingDir))}`);
|
|
4583
4577
|
lines.push(`${theme.primary('Autosave')}: ${this.autosaveEnabled ? theme.success('on') : theme.ui.muted('off')}`);
|
|
4578
|
+
lines.push(`${theme.primary('Auto-continue')}: ${this.autoContinueEnabled ? theme.success('on') : theme.ui.muted('off')}`);
|
|
4584
4579
|
lines.push(`${theme.primary('Verification')}: ${this.verificationEnabled ? theme.success('on') : theme.ui.muted('off')}`);
|
|
4585
4580
|
lines.push(`${theme.primary('Approvals')}: ${theme.ui.muted(this.describeEditGuardMode())}`);
|
|
4586
4581
|
lines.push(`${theme.primary('Critical approvals')}: ${this.criticalApprovalMode === 'approval' ? theme.warning('ask') : theme.ui.muted('auto')}`);
|
|
@@ -4716,6 +4711,52 @@ export class InteractiveShell {
|
|
|
4716
4711
|
}
|
|
4717
4712
|
display.showWarning('Usage: /approvals [auto|ask|status]');
|
|
4718
4713
|
}
|
|
4714
|
+
handlePlanCommand(input) {
|
|
4715
|
+
const action = input.split(/\s+/)[1]?.toLowerCase() || 'show';
|
|
4716
|
+
const hasPlan = this.planOverlay.hasPlan();
|
|
4717
|
+
const syncPanel = () => {
|
|
4718
|
+
this.applyPlanOverlay();
|
|
4719
|
+
this.syncRendererInput();
|
|
4720
|
+
};
|
|
4721
|
+
switch (action) {
|
|
4722
|
+
case 'show':
|
|
4723
|
+
case 'on':
|
|
4724
|
+
if (!hasPlan) {
|
|
4725
|
+
display.showInfo('No active plan. The panel will populate when a planning tool updates the plan.');
|
|
4726
|
+
return;
|
|
4727
|
+
}
|
|
4728
|
+
this.planOverlay.show();
|
|
4729
|
+
syncPanel();
|
|
4730
|
+
display.showInfo('Plan panel pinned. Use /plan hide to minimize.');
|
|
4731
|
+
return;
|
|
4732
|
+
case 'hide':
|
|
4733
|
+
case 'off':
|
|
4734
|
+
if (!hasPlan) {
|
|
4735
|
+
display.showInfo('No active plan to hide.');
|
|
4736
|
+
return;
|
|
4737
|
+
}
|
|
4738
|
+
this.planOverlay.hide();
|
|
4739
|
+
syncPanel();
|
|
4740
|
+
display.showInfo('Plan panel hidden. Use /plan show to restore.');
|
|
4741
|
+
return;
|
|
4742
|
+
case 'clear':
|
|
4743
|
+
if (!hasPlan) {
|
|
4744
|
+
display.showInfo('No active plan to clear.');
|
|
4745
|
+
return;
|
|
4746
|
+
}
|
|
4747
|
+
this.planOverlay.clear();
|
|
4748
|
+
syncPanel();
|
|
4749
|
+
display.showInfo('Plan cleared.');
|
|
4750
|
+
return;
|
|
4751
|
+
case 'status':
|
|
4752
|
+
case 'view':
|
|
4753
|
+
case 'text':
|
|
4754
|
+
display.showSystemMessage(this.planOverlay.asText());
|
|
4755
|
+
return;
|
|
4756
|
+
default:
|
|
4757
|
+
display.showWarning('Usage: /plan [show|hide|clear|status]');
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4719
4760
|
handlePermissionsCommand() {
|
|
4720
4761
|
const lines = [];
|
|
4721
4762
|
lines.push(theme.bold('Tool Permissions'));
|
|
@@ -4736,67 +4777,6 @@ export class InteractiveShell {
|
|
|
4736
4777
|
lines.push(theme.ui.muted('Use /tools to manage which tool suites are enabled.'));
|
|
4737
4778
|
display.showSystemMessage(lines.join('\n'));
|
|
4738
4779
|
}
|
|
4739
|
-
async handleUpdateCommand(input) {
|
|
4740
|
-
const tokens = input.split(/\s+/).slice(1);
|
|
4741
|
-
const subcommand = tokens[0]?.toLowerCase();
|
|
4742
|
-
const prefs = loadSessionPreferences();
|
|
4743
|
-
const currentPref = prefs.autoUpdate;
|
|
4744
|
-
const prefLabel = currentPref === true ? 'always update' : currentPref === false ? 'always skip' : 'notify only';
|
|
4745
|
-
// Show status or help
|
|
4746
|
-
if (!subcommand || subcommand === 'status') {
|
|
4747
|
-
const lines = [];
|
|
4748
|
-
lines.push(theme.bold('Update Settings'));
|
|
4749
|
-
lines.push('');
|
|
4750
|
-
lines.push(`Current preference: ${theme.info(prefLabel)}`);
|
|
4751
|
-
lines.push('');
|
|
4752
|
-
lines.push(theme.secondary('Commands:'));
|
|
4753
|
-
lines.push(' /update check - Check for updates and install immediately');
|
|
4754
|
-
lines.push(' /update auto - Always auto-update in background');
|
|
4755
|
-
lines.push(' /update skip - Never auto-update (silent)');
|
|
4756
|
-
lines.push(' /update notify - Show notification only (default)');
|
|
4757
|
-
display.showSystemMessage(lines.join('\n'));
|
|
4758
|
-
return;
|
|
4759
|
-
}
|
|
4760
|
-
if (subcommand === 'check') {
|
|
4761
|
-
// Force check and perform update immediately (non-interactive)
|
|
4762
|
-
try {
|
|
4763
|
-
const { checkForUpdates, performUpdate } = await import('../core/updateChecker.js');
|
|
4764
|
-
const currentVersion = this.version || this.getPackageInfo().version || '1.7.458';
|
|
4765
|
-
const updateInfo = await checkForUpdates(currentVersion);
|
|
4766
|
-
if (!updateInfo) {
|
|
4767
|
-
display.showWarning('Unable to check for updates (network issue or timeout).');
|
|
4768
|
-
return;
|
|
4769
|
-
}
|
|
4770
|
-
if (!updateInfo.updateAvailable) {
|
|
4771
|
-
display.showSuccess(`You're on the latest version (v${updateInfo.current}).`);
|
|
4772
|
-
return;
|
|
4773
|
-
}
|
|
4774
|
-
// Show update info and perform update immediately (no interactive prompt)
|
|
4775
|
-
display.showInfo(`Update available: v${updateInfo.current} → v${updateInfo.latest}`);
|
|
4776
|
-
await performUpdate(updateInfo, (msg) => this.streamEventBlock(msg));
|
|
4777
|
-
}
|
|
4778
|
-
catch (error) {
|
|
4779
|
-
display.showError(`Update check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
4780
|
-
}
|
|
4781
|
-
return;
|
|
4782
|
-
}
|
|
4783
|
-
if (subcommand === 'auto' || subcommand === 'always') {
|
|
4784
|
-
saveSessionPreferences({ autoUpdate: true });
|
|
4785
|
-
display.showSuccess('Auto-update enabled. Updates will install automatically.');
|
|
4786
|
-
return;
|
|
4787
|
-
}
|
|
4788
|
-
if (subcommand === 'skip' || subcommand === 'never' || subcommand === 'off') {
|
|
4789
|
-
saveSessionPreferences({ autoUpdate: false });
|
|
4790
|
-
display.showInfo('Auto-update disabled. Updates will be skipped silently.');
|
|
4791
|
-
return;
|
|
4792
|
-
}
|
|
4793
|
-
if (subcommand === 'ask' || subcommand === 'prompt' || subcommand === 'reset' || subcommand === 'notify') {
|
|
4794
|
-
saveSessionPreferences({ autoUpdate: null });
|
|
4795
|
-
display.showInfo('Update preference reset. You will see a notification when updates are available.');
|
|
4796
|
-
return;
|
|
4797
|
-
}
|
|
4798
|
-
display.showWarning('Usage: /update [check|auto|skip|notify|status]');
|
|
4799
|
-
}
|
|
4800
4780
|
handleInitCommand(input) {
|
|
4801
4781
|
const tokens = input.split(/\s+/).slice(1);
|
|
4802
4782
|
const confirm = tokens[0]?.toLowerCase() === 'confirm';
|
|
@@ -5821,7 +5801,6 @@ export class InteractiveShell {
|
|
|
5821
5801
|
}
|
|
5822
5802
|
this.isProcessing = true;
|
|
5823
5803
|
this.uiUpdates.setMode('processing');
|
|
5824
|
-
this.streamingTokenCount = 0; // Reset token counter for new request
|
|
5825
5804
|
this.terminalInput.setStreaming(true);
|
|
5826
5805
|
// Keep the persistent input/control bar active as we transition into streaming.
|
|
5827
5806
|
this.syncRendererInput();
|
|
@@ -5836,7 +5815,6 @@ export class InteractiveShell {
|
|
|
5836
5815
|
this.currentTaskType = classifyTaskType(request);
|
|
5837
5816
|
this.currentToolCalls = [];
|
|
5838
5817
|
this.clearToolUsageMeta();
|
|
5839
|
-
this.renderer?.setActivity('Starting...');
|
|
5840
5818
|
this.uiAdapter.startProcessing('Working on your request');
|
|
5841
5819
|
this.setProcessingStatus();
|
|
5842
5820
|
this.beginAiRuntime();
|
|
@@ -5892,6 +5870,8 @@ export class InteractiveShell {
|
|
|
5892
5870
|
clearActionHistory();
|
|
5893
5871
|
this.lastFailure = null;
|
|
5894
5872
|
}
|
|
5873
|
+
// Run post-hoc AI flow sanity check to catch "looks right but wrong" responses
|
|
5874
|
+
this.analyzeAiFlowForRun(request, responseText, requestStartTime);
|
|
5895
5875
|
}
|
|
5896
5876
|
catch (error) {
|
|
5897
5877
|
const handled = this.handleProviderError(error, () => this.processRequest(request));
|
|
@@ -5911,7 +5891,7 @@ export class InteractiveShell {
|
|
|
5911
5891
|
this.responseRendered = true;
|
|
5912
5892
|
}
|
|
5913
5893
|
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5914
|
-
display.stopThinking(
|
|
5894
|
+
display.stopThinking();
|
|
5915
5895
|
this.uiUpdates.setMode('processing');
|
|
5916
5896
|
this.stopStreamingHeartbeat('complete', { quiet: true });
|
|
5917
5897
|
this.endAiRuntime();
|
|
@@ -5962,7 +5942,6 @@ export class InteractiveShell {
|
|
|
5962
5942
|
this.clearToolUsageMeta();
|
|
5963
5943
|
this.isProcessing = true;
|
|
5964
5944
|
this.uiUpdates.setMode('processing');
|
|
5965
|
-
this.streamingTokenCount = 0; // Reset token counter for new request
|
|
5966
5945
|
this.terminalInput.setStreaming(true);
|
|
5967
5946
|
if (this.suppressNextNetworkReset) {
|
|
5968
5947
|
this.suppressNextNetworkReset = false;
|
|
@@ -6128,7 +6107,7 @@ What's the next action?`;
|
|
|
6128
6107
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
6129
6108
|
}
|
|
6130
6109
|
catch (error) {
|
|
6131
|
-
display.stopThinking(
|
|
6110
|
+
display.stopThinking();
|
|
6132
6111
|
// Handle context overflow specially - the agent should auto-recover
|
|
6133
6112
|
// but if it propagates here, we continue the loop
|
|
6134
6113
|
if (this.isContextOverflowError(error)) {
|
|
@@ -6370,6 +6349,43 @@ What's the next action?`;
|
|
|
6370
6349
|
const parts = candidates.filter((part) => typeof part === 'string' && part.trim().length > 0);
|
|
6371
6350
|
return parts.join('\n').trim();
|
|
6372
6351
|
}
|
|
6352
|
+
/**
|
|
6353
|
+
* Post-run sanity check to catch hallucinated "looks right" answers.
|
|
6354
|
+
* Compares the prompt/response against per-run tool usage and surfaces risks.
|
|
6355
|
+
*/
|
|
6356
|
+
analyzeAiFlowForRun(prompt, responseText, startedAt) {
|
|
6357
|
+
const toolHistory = this.runtimeSession.toolRuntime.getToolHistory?.();
|
|
6358
|
+
if (!toolHistory) {
|
|
6359
|
+
return;
|
|
6360
|
+
}
|
|
6361
|
+
const diffSnapshots = this.runtimeSession.toolRuntime.getDiffSnapshots?.() ?? [];
|
|
6362
|
+
const recentDiffs = diffSnapshots
|
|
6363
|
+
.filter((snapshot) => snapshot.timestamp >= startedAt)
|
|
6364
|
+
.map(({ command, output }) => ({ command, output }));
|
|
6365
|
+
const assessment = assessAiFlow({
|
|
6366
|
+
prompt,
|
|
6367
|
+
response: responseText,
|
|
6368
|
+
startedAt,
|
|
6369
|
+
toolHistory,
|
|
6370
|
+
diffSnapshots: recentDiffs,
|
|
6371
|
+
});
|
|
6372
|
+
this.reportAiFlowAssessment(assessment);
|
|
6373
|
+
}
|
|
6374
|
+
reportAiFlowAssessment(assessment) {
|
|
6375
|
+
if (!assessment || assessment.risks.length === 0) {
|
|
6376
|
+
return;
|
|
6377
|
+
}
|
|
6378
|
+
display.showSystemMessage('⚠️ AI flow check: potential unverified completion detected.');
|
|
6379
|
+
for (const risk of assessment.risks) {
|
|
6380
|
+
const detail = risk.details ? ` — ${risk.details}` : '';
|
|
6381
|
+
display.showWarning(`${risk.summary}${detail}`);
|
|
6382
|
+
}
|
|
6383
|
+
if (assessment.recommendations.length > 0) {
|
|
6384
|
+
const uniqueRecommendations = Array.from(new Set(assessment.recommendations));
|
|
6385
|
+
const advice = uniqueRecommendations.slice(0, 3).map((rec, idx) => `${idx + 1}. ${rec}`).join('\n');
|
|
6386
|
+
display.showSystemMessage(`Follow-ups to de-risk this run:\n${advice}`);
|
|
6387
|
+
}
|
|
6388
|
+
}
|
|
6373
6389
|
runAutoQualityChecks(trigger, assistantResponse, verificationContext) {
|
|
6374
6390
|
if (!this.verificationEnabled) {
|
|
6375
6391
|
return;
|
|
@@ -6750,41 +6766,21 @@ Return ONLY JSON array:
|
|
|
6750
6766
|
maxTokens: this.sessionState.maxTokens,
|
|
6751
6767
|
systemPrompt: this.buildSystemPrompt(),
|
|
6752
6768
|
reasoningEffort: this.sessionState.reasoningEffort,
|
|
6769
|
+
autoContinue: this.autoContinueEnabled,
|
|
6753
6770
|
};
|
|
6754
6771
|
this.agent = this.runtimeSession.createAgent(selection, {
|
|
6755
|
-
onRequestReceived: (requestPreview) => {
|
|
6756
|
-
const normalized = requestPreview?.trim();
|
|
6757
|
-
const activity = normalized ? `Working: ${normalized}` : 'Working';
|
|
6758
|
-
this.renderer?.setActivity(activity);
|
|
6759
|
-
},
|
|
6760
|
-
onBeforeFirstToolCall: (toolNames) => {
|
|
6761
|
-
const primaryTool = toolNames[0];
|
|
6762
|
-
if (primaryTool) {
|
|
6763
|
-
this.renderer?.setActivity(`Running ${primaryTool}`);
|
|
6764
|
-
}
|
|
6765
|
-
return undefined;
|
|
6766
|
-
},
|
|
6767
6772
|
onStreamChunk: (chunk, type) => {
|
|
6768
6773
|
this.handleStreamChunk(chunk, type ?? 'content');
|
|
6769
6774
|
},
|
|
6775
|
+
onStreamFallback: (info) => this.handleStreamingFallback(info),
|
|
6770
6776
|
onAssistantMessage: (content, metadata) => {
|
|
6771
6777
|
const enriched = this.buildDisplayMetadata(metadata);
|
|
6772
6778
|
const streamedVisible = metadata.wasStreamed && this.streamingContentSeen && !this.streamingOutputSuppressed;
|
|
6773
6779
|
let renderedFinal = false;
|
|
6774
|
-
// Update streaming token count from usage info (more accurate than chunk counting)
|
|
6775
|
-
if (metadata.usage) {
|
|
6776
|
-
const totalTokens = this.totalTokens(metadata.usage);
|
|
6777
|
-
if (totalTokens !== null && totalTokens > this.streamingTokenCount) {
|
|
6778
|
-
this.streamingTokenCount = totalTokens;
|
|
6779
|
-
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6780
|
-
}
|
|
6781
|
-
}
|
|
6782
6780
|
// Update spinner based on message type
|
|
6783
6781
|
if (metadata.isFinal) {
|
|
6784
6782
|
const parsed = this.splitThinkingResponse(content);
|
|
6785
|
-
|
|
6786
|
-
// Don't fall back to original content with raw <thinking> tags
|
|
6787
|
-
const finalContent = parsed ? parsed.response?.trim() : content;
|
|
6783
|
+
const finalContent = parsed?.response?.trim() || content;
|
|
6788
6784
|
const thoughtContent = parsed?.thinking?.trim() || null;
|
|
6789
6785
|
// Show the response if it wasn't already rendered during streaming
|
|
6790
6786
|
if (!streamedVisible) {
|
|
@@ -6887,6 +6883,12 @@ Return ONLY JSON array:
|
|
|
6887
6883
|
this.updateStatusMessage('Retrying with reduced context...');
|
|
6888
6884
|
this.syncRendererInput();
|
|
6889
6885
|
},
|
|
6886
|
+
onAutoContinue: (attempt, maxAttempts, _message) => {
|
|
6887
|
+
// Show auto-continue progress in UI
|
|
6888
|
+
display.showSystemMessage(`🔄 Auto-continue (${attempt}/${maxAttempts}): Model expressed intent, prompting to act...`);
|
|
6889
|
+
this.updateStatusMessage('Auto-continuing...');
|
|
6890
|
+
this.syncRendererInput();
|
|
6891
|
+
},
|
|
6890
6892
|
onCancelled: () => {
|
|
6891
6893
|
// Update UI to show operation was cancelled
|
|
6892
6894
|
display.showWarning('Operation cancelled.');
|
|
@@ -6895,42 +6897,10 @@ Return ONLY JSON array:
|
|
|
6895
6897
|
this.updateStatusMessage(null);
|
|
6896
6898
|
this.terminalInput.setStreaming(false);
|
|
6897
6899
|
},
|
|
6898
|
-
onToolExecution: (toolName, isStart, args) => {
|
|
6899
|
-
// Update activity status to show what tool is being executed
|
|
6900
|
-
if (isStart) {
|
|
6901
|
-
// Show more specific activity for long-running tools
|
|
6902
|
-
let activity = `Running ${toolName}`;
|
|
6903
|
-
if (toolName === 'execute_bash' && args?.['command']) {
|
|
6904
|
-
const cmd = String(args['command']).slice(0, 40);
|
|
6905
|
-
activity = `$ ${cmd}${String(args['command']).length > 40 ? '...' : ''}`;
|
|
6906
|
-
}
|
|
6907
|
-
else if (toolName === 'read_file' && args?.['file_path']) {
|
|
6908
|
-
const path = String(args['file_path']).split('/').pop() || args['file_path'];
|
|
6909
|
-
activity = `Reading ${path}`;
|
|
6910
|
-
}
|
|
6911
|
-
this.renderer?.setActivity(activity);
|
|
6912
|
-
// Estimate tokens for tool call (~50 tokens per call)
|
|
6913
|
-
this.streamingTokenCount += 50;
|
|
6914
|
-
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6915
|
-
}
|
|
6916
|
-
else {
|
|
6917
|
-
// Tool finished - estimate result tokens (~100 per result)
|
|
6918
|
-
this.streamingTokenCount += 100;
|
|
6919
|
-
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6920
|
-
// Reset to thinking state while model generates next response
|
|
6921
|
-
this.renderer?.setActivity('Thinking');
|
|
6922
|
-
}
|
|
6923
|
-
},
|
|
6924
6900
|
onVerificationNeeded: (response, context) => {
|
|
6925
6901
|
this.lastAssistantResponse = response;
|
|
6926
6902
|
void this.runAutoQualityChecks('verification', response, context);
|
|
6927
6903
|
},
|
|
6928
|
-
// Retry notification for transient errors
|
|
6929
|
-
onRetrying: (attempt, maxAttempts, error) => {
|
|
6930
|
-
const shortError = error.message.slice(0, 100);
|
|
6931
|
-
display.showSystemMessage(`⚡ Retry ${attempt}/${maxAttempts}: ${shortError}${error.message.length > 100 ? '...' : ''}`);
|
|
6932
|
-
this.renderer?.setActivity(`Retrying (${attempt}/${maxAttempts})...`);
|
|
6933
|
-
},
|
|
6934
6904
|
});
|
|
6935
6905
|
// Register global AI enhancer for explore tool - uses active model by default
|
|
6936
6906
|
this.registerExploreAIEnhancer();
|
|
@@ -7025,30 +6995,20 @@ Return ONLY JSON array:
|
|
|
7025
6995
|
return lines.join('\n').trim();
|
|
7026
6996
|
}
|
|
7027
6997
|
buildThinkingDirective() {
|
|
7028
|
-
// Base requirement: ALWAYS think before acting (applies to all modes)
|
|
7029
|
-
const baseRequirement = [
|
|
7030
|
-
'CRITICAL: Before calling ANY tool, ALWAYS output a <thinking>...</thinking> block explaining:',
|
|
7031
|
-
'1. What you understand the user is asking',
|
|
7032
|
-
'2. Your approach/plan to solve it',
|
|
7033
|
-
'3. Which tools you will use and why',
|
|
7034
|
-
'This thinking block MUST appear before your first tool call in every response.',
|
|
7035
|
-
].join('\n');
|
|
7036
6998
|
switch (this.thinkingMode) {
|
|
7037
6999
|
case 'extended':
|
|
7038
7000
|
return [
|
|
7039
|
-
|
|
7040
|
-
'',
|
|
7041
|
-
'
|
|
7042
|
-
'
|
|
7043
|
-
'
|
|
7001
|
+
'Extended thinking mode is enabled. Format every reply as:',
|
|
7002
|
+
'<thinking>',
|
|
7003
|
+
'Detailed multi-step reasoning (reference tool runs/files when relevant, keep secrets redacted, no code blocks unless citing filenames).',
|
|
7004
|
+
'</thinking>',
|
|
7005
|
+
'<response>',
|
|
7006
|
+
'Final answer with actionable next steps and any code/commands requested.',
|
|
7007
|
+
'</response>',
|
|
7044
7008
|
].join('\n');
|
|
7045
7009
|
case 'balanced':
|
|
7046
7010
|
default:
|
|
7047
|
-
return
|
|
7048
|
-
baseRequirement,
|
|
7049
|
-
'',
|
|
7050
|
-
'Balanced mode: Keep thinking concise (2-4 sentences) but always present.',
|
|
7051
|
-
].join('\n');
|
|
7011
|
+
return 'Balanced thinking mode: include a short <thinking>...</thinking> block before <response> when the reasoning is non-trivial; skip it for simple answers.';
|
|
7052
7012
|
}
|
|
7053
7013
|
}
|
|
7054
7014
|
buildDisplayMetadata(metadata) {
|
|
@@ -7139,14 +7099,13 @@ Return ONLY JSON array:
|
|
|
7139
7099
|
});
|
|
7140
7100
|
cleanupOverlayActive = true;
|
|
7141
7101
|
const triggerReason = trigger?.reason ?? 'Context optimization';
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
}
|
|
7102
|
+
const limitLabel = resolvedWindowTokens
|
|
7103
|
+
? `of ${resolvedWindowTokens.toLocaleString('en-US')} tokens`
|
|
7104
|
+
: 'of estimated context';
|
|
7105
|
+
display.showSystemMessage([
|
|
7106
|
+
`Context usage: ${resolvedTotalTokens.toLocaleString('en-US')} ${limitLabel}`,
|
|
7107
|
+
`(${percentUsed}% full). Auto-compacting${triggerReason ? `: ${triggerReason}` : '...'}`,
|
|
7108
|
+
].join(' '));
|
|
7150
7109
|
const result = await contextManager.intelligentCompact(history);
|
|
7151
7110
|
let afterStats = contextManager.getStats(result.compacted);
|
|
7152
7111
|
let appliedHistory = result.compacted;
|
|
@@ -7173,18 +7132,10 @@ Return ONLY JSON array:
|
|
|
7173
7132
|
}
|
|
7174
7133
|
}
|
|
7175
7134
|
if (!changed) {
|
|
7176
|
-
// Hide compacting status before showing info message
|
|
7177
|
-
if (this.renderer) {
|
|
7178
|
-
this.renderer.hideCompactingStatus();
|
|
7179
|
-
}
|
|
7180
7135
|
display.showInfo('Context compaction completed but no changes were applied.');
|
|
7181
7136
|
return;
|
|
7182
7137
|
}
|
|
7183
7138
|
if (!this.hasMeaningfulCompaction(changed, bestTokenSavings, bestPercentSavings, trigger?.forced)) {
|
|
7184
|
-
// Hide compacting status before showing info message
|
|
7185
|
-
if (this.renderer) {
|
|
7186
|
-
this.renderer.hideCompactingStatus();
|
|
7187
|
-
}
|
|
7188
7139
|
display.showInfo('Auto-compaction completed but did not meaningfully reduce context size. Keeping existing history.');
|
|
7189
7140
|
return;
|
|
7190
7141
|
}
|
|
@@ -7200,18 +7151,15 @@ Return ONLY JSON array:
|
|
|
7200
7151
|
this.updateContextUsage(newPercentUsed, CONTEXT_AUTOCOMPACT_PERCENT);
|
|
7201
7152
|
this.lastContextWarningLevel = this.getContextWarningLevel(newPercentUsed);
|
|
7202
7153
|
this.refreshStatusLine(true);
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
else {
|
|
7213
|
-
display.showSystemMessage('══ Conversation compacted · ctrl+o for history ═');
|
|
7214
|
-
}
|
|
7154
|
+
const primarySignal = result.analysis.signals[0]?.reason ?? triggerReason;
|
|
7155
|
+
const savingsLabel = bestTokenSavings > 0
|
|
7156
|
+
? `(saved ~${bestTokenSavings.toLocaleString('en-US')} tokens, ~${bestPercentSavings.toFixed(1)}%)`
|
|
7157
|
+
: '(no token savings)';
|
|
7158
|
+
display.showSystemMessage([
|
|
7159
|
+
`Context compacted: ${beforeStats.percentage}% → ${afterStats.percentage}%`,
|
|
7160
|
+
savingsLabel,
|
|
7161
|
+
primarySignal ? `Reason: ${primarySignal}.` : '',
|
|
7162
|
+
].filter(Boolean).join(' '));
|
|
7215
7163
|
this.recordContextCompaction({
|
|
7216
7164
|
timestamp: Date.now(),
|
|
7217
7165
|
source: trigger?.source ?? 'auto',
|
|
@@ -7225,20 +7173,12 @@ Return ONLY JSON array:
|
|
|
7225
7173
|
});
|
|
7226
7174
|
}
|
|
7227
7175
|
catch (error) {
|
|
7228
|
-
// Hide compacting status animation on error
|
|
7229
|
-
if (this.renderer) {
|
|
7230
|
-
this.renderer.hideCompactingStatus();
|
|
7231
|
-
}
|
|
7232
7176
|
display.showError('Context compaction failed.', error);
|
|
7233
7177
|
}
|
|
7234
7178
|
finally {
|
|
7235
7179
|
if (cleanupOverlayActive) {
|
|
7236
7180
|
this.statusTracker.clearOverride(cleanupStatusId);
|
|
7237
7181
|
}
|
|
7238
|
-
// Ensure compacting status is cleared
|
|
7239
|
-
if (this.renderer) {
|
|
7240
|
-
this.renderer.hideCompactingStatus();
|
|
7241
|
-
}
|
|
7242
7182
|
this.cleanupInProgress = false;
|
|
7243
7183
|
this.contextCompactionInFlight = false;
|
|
7244
7184
|
}
|
|
@@ -7540,6 +7480,28 @@ Return ONLY JSON array:
|
|
|
7540
7480
|
const message = error instanceof Error ? error.message : String(error);
|
|
7541
7481
|
display.showError(message);
|
|
7542
7482
|
}
|
|
7483
|
+
handleStreamingFallback(info) {
|
|
7484
|
+
const promptBlock = detectPromptBlockError(info.error ?? info.message);
|
|
7485
|
+
if (promptBlock) {
|
|
7486
|
+
this.handlePromptBlock(promptBlock);
|
|
7487
|
+
this.finishStreamingFormatter('Prompt blocked mid-stream', { mode: 'update' });
|
|
7488
|
+
display.showSystemMessage('Retrying without streaming, but the provider will likely block again until you rephrase.');
|
|
7489
|
+
this.startStreamingHeartbeat('Re-running without streaming');
|
|
7490
|
+
this.requestPromptRefresh(true);
|
|
7491
|
+
return;
|
|
7492
|
+
}
|
|
7493
|
+
const detailText = info.message?.trim() ?? '';
|
|
7494
|
+
const detail = detailText ? ` ${detailText}` : '';
|
|
7495
|
+
const reason = info.reason ? ` (${info.reason.replace(/-/g, ' ')})` : '';
|
|
7496
|
+
const partialNote = info.partialResponse
|
|
7497
|
+
? ' Partial stream captured before failure; continuing from it without restarting.'
|
|
7498
|
+
: '';
|
|
7499
|
+
const baseMessage = 'Streaming interrupted, retrying without streaming';
|
|
7500
|
+
display.showWarning(`${baseMessage}${reason}.${detail}${partialNote}`.trim());
|
|
7501
|
+
this.finishStreamingFormatter('Stream interrupted - retrying without streaming', { mode: 'update' });
|
|
7502
|
+
this.startStreamingHeartbeat('Fallback in progress');
|
|
7503
|
+
this.requestPromptRefresh(true);
|
|
7504
|
+
}
|
|
7543
7505
|
handleProviderError(error, retryAction) {
|
|
7544
7506
|
const promptBlock = detectPromptBlockError(error);
|
|
7545
7507
|
if (promptBlock) {
|