erosolar-cli 2.1.172 → 2.1.174
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 +21 -3
- 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 +83 -24
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +499 -248
- package/dist/core/agent.js.map +1 -1
- package/dist/core/preferences.d.ts +1 -0
- package/dist/core/preferences.d.ts.map +1 -1
- package/dist/core/preferences.js +8 -1
- package/dist/core/preferences.js.map +1 -1
- package/dist/core/reliabilityPrompt.d.ts +9 -0
- package/dist/core/reliabilityPrompt.d.ts.map +1 -0
- package/dist/core/reliabilityPrompt.js +31 -0
- package/dist/core/reliabilityPrompt.js.map +1 -0
- package/dist/core/schemaValidator.js +3 -3
- package/dist/core/schemaValidator.js.map +1 -1
- package/dist/core/toolPreconditions.d.ts +0 -11
- package/dist/core/toolPreconditions.d.ts.map +1 -1
- package/dist/core/toolPreconditions.js +33 -164
- package/dist/core/toolPreconditions.js.map +1 -1
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +9 -114
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/core/updateChecker.d.ts +61 -1
- package/dist/core/updateChecker.d.ts.map +1 -1
- package/dist/core/updateChecker.js +147 -3
- package/dist/core/updateChecker.js.map +1 -1
- package/dist/headless/headlessApp.d.ts.map +1 -1
- package/dist/headless/headlessApp.js +0 -39
- package/dist/headless/headlessApp.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/providers/openaiResponsesProvider.d.ts.map +1 -1
- package/dist/providers/openaiResponsesProvider.js +79 -74
- package/dist/providers/openaiResponsesProvider.js.map +1 -1
- package/dist/runtime/agentController.d.ts.map +1 -1
- package/dist/runtime/agentController.js +6 -3
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/runtime/agentSession.d.ts +0 -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 +11 -18
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +273 -291
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.d.ts.map +1 -1
- package/dist/shell/shellApp.js +7 -1
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/shell/systemPrompt.d.ts.map +1 -1
- package/dist/shell/systemPrompt.js +4 -15
- package/dist/shell/systemPrompt.js.map +1 -1
- package/dist/subagents/taskRunner.js +2 -1
- package/dist/subagents/taskRunner.js.map +1 -1
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +101 -8
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/diffUtils.d.ts +8 -2
- package/dist/tools/diffUtils.d.ts.map +1 -1
- package/dist/tools/diffUtils.js +72 -13
- package/dist/tools/diffUtils.js.map +1 -1
- package/dist/tools/grepTools.d.ts.map +1 -1
- package/dist/tools/grepTools.js +10 -2
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/planningTools.d.ts +0 -10
- package/dist/tools/planningTools.d.ts.map +1 -1
- package/dist/tools/planningTools.js +0 -16
- package/dist/tools/planningTools.js.map +1 -1
- package/dist/tools/searchTools.d.ts.map +1 -1
- package/dist/tools/searchTools.js +4 -2
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/ui/PromptController.d.ts +1 -4
- package/dist/ui/PromptController.d.ts.map +1 -1
- package/dist/ui/PromptController.js +1 -7
- package/dist/ui/PromptController.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +292 -28
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +1513 -121
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/UnifiedUIController.d.ts +81 -0
- package/dist/ui/UnifiedUIController.d.ts.map +1 -0
- package/dist/ui/UnifiedUIController.js +212 -0
- package/dist/ui/UnifiedUIController.js.map +1 -0
- package/dist/ui/UnifiedUIRenderer.d.ts +133 -30
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +939 -370
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/animatedStatus.d.ts +128 -6
- package/dist/ui/animatedStatus.d.ts.map +1 -1
- package/dist/ui/animatedStatus.js +383 -50
- package/dist/ui/animatedStatus.js.map +1 -1
- package/dist/ui/animation/AnimationScheduler.d.ts +192 -0
- package/dist/ui/animation/AnimationScheduler.d.ts.map +1 -0
- package/dist/ui/animation/AnimationScheduler.js +432 -0
- package/dist/ui/animation/AnimationScheduler.js.map +1 -0
- package/dist/ui/display.d.ts +182 -26
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +678 -97
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/inPlaceUpdater.d.ts +181 -0
- package/dist/ui/inPlaceUpdater.d.ts.map +1 -0
- package/dist/ui/inPlaceUpdater.js +515 -0
- package/dist/ui/inPlaceUpdater.js.map +1 -0
- package/dist/ui/interrupts/InterruptManager.d.ts +142 -0
- package/dist/ui/interrupts/InterruptManager.d.ts.map +1 -0
- package/dist/ui/interrupts/InterruptManager.js +439 -0
- package/dist/ui/interrupts/InterruptManager.js.map +1 -0
- package/dist/ui/layout.d.ts +0 -1
- package/dist/ui/layout.d.ts.map +1 -1
- package/dist/ui/layout.js +0 -12
- package/dist/ui/layout.js.map +1 -1
- package/dist/ui/orchestration/UIUpdateCoordinator.d.ts +61 -7
- package/dist/ui/orchestration/UIUpdateCoordinator.d.ts.map +1 -1
- package/dist/ui/orchestration/UIUpdateCoordinator.js +232 -20
- package/dist/ui/orchestration/UIUpdateCoordinator.js.map +1 -1
- package/dist/ui/shortcutsHelp.d.ts.map +1 -1
- package/dist/ui/shortcutsHelp.js +0 -1
- package/dist/ui/shortcutsHelp.js.map +1 -1
- package/dist/ui/telemetry/ResponseTracker.d.ts +22 -0
- package/dist/ui/telemetry/ResponseTracker.d.ts.map +1 -0
- package/dist/ui/telemetry/ResponseTracker.js +60 -0
- package/dist/ui/telemetry/ResponseTracker.js.map +1 -0
- package/dist/ui/telemetry/UITelemetry.d.ts +181 -0
- package/dist/ui/telemetry/UITelemetry.d.ts.map +1 -0
- package/dist/ui/telemetry/UITelemetry.js +446 -0
- package/dist/ui/telemetry/UITelemetry.js.map +1 -0
- package/dist/ui/unified/index.d.ts +30 -1
- package/dist/ui/unified/index.d.ts.map +1 -1
- package/dist/ui/unified/index.js +45 -2
- package/dist/ui/unified/index.js.map +1 -1
- package/dist/ui/unified/layout.d.ts +12 -0
- package/dist/ui/unified/layout.d.ts.map +1 -0
- package/dist/ui/unified/layout.js +96 -0
- package/dist/ui/unified/layout.js.map +1 -0
- package/package.json +2 -3
- package/dist/StringUtils.d.ts +0 -8
- package/dist/StringUtils.d.ts.map +0 -1
- package/dist/StringUtils.js +0 -11
- package/dist/StringUtils.js.map +0 -1
- package/dist/core/aiFlowSupervisor.d.ts +0 -44
- package/dist/core/aiFlowSupervisor.d.ts.map +0 -1
- package/dist/core/aiFlowSupervisor.js +0 -299
- package/dist/core/aiFlowSupervisor.js.map +0 -1
- package/dist/core/cliTestHarness.d.ts +0 -200
- package/dist/core/cliTestHarness.d.ts.map +0 -1
- package/dist/core/cliTestHarness.js +0 -549
- package/dist/core/cliTestHarness.js.map +0 -1
- package/dist/core/testUtils.d.ts +0 -121
- package/dist/core/testUtils.d.ts.map +0 -1
- package/dist/core/testUtils.js +0 -235
- package/dist/core/testUtils.js.map +0 -1
- package/dist/core/toolValidation.d.ts +0 -116
- package/dist/core/toolValidation.d.ts.map +0 -1
- package/dist/core/toolValidation.js +0 -282
- package/dist/core/toolValidation.js.map +0 -1
- package/dist/ui/planOverlay.d.ts +0 -28
- package/dist/ui/planOverlay.d.ts.map +0 -1
- package/dist/ui/planOverlay.js +0 -156
- package/dist/ui/planOverlay.js.map +0 -1
- package/dist/ui/streamingFormatter.d.ts +0 -30
- package/dist/ui/streamingFormatter.d.ts.map +0 -1
- package/dist/ui/streamingFormatter.js +0 -91
- package/dist/ui/streamingFormatter.js.map +0 -1
- package/dist/utils/errorUtils.d.ts +0 -16
- package/dist/utils/errorUtils.d.ts.map +0 -1
- package/dist/utils/errorUtils.js +0 -66
- package/dist/utils/errorUtils.js.map +0 -1
|
@@ -7,7 +7,6 @@ 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';
|
|
11
10
|
import { getContextWindowTokens } from '../core/contextWindow.js';
|
|
12
11
|
import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
|
|
13
12
|
import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
|
|
@@ -19,7 +18,6 @@ import { detectNetworkError } from '../core/errors/networkErrors.js';
|
|
|
19
18
|
import { buildWorkspaceContext } from '../workspace.js';
|
|
20
19
|
import { buildInteractiveSystemPrompt } from './systemPrompt.js';
|
|
21
20
|
import { getTaskCompletionDetector, resetTaskCompletionDetector, } from './taskCompletionDetector.js';
|
|
22
|
-
import { assessAiFlow } from '../core/aiFlowSupervisor.js';
|
|
23
21
|
import { discoverAllModels, quickCheckProviders, getCachedDiscoveredModels, sortModelsByPriority } from '../core/modelDiscovery.js';
|
|
24
22
|
import { getModels, getSlashCommands, getProviders } from '../core/agentSchemaLoader.js';
|
|
25
23
|
import { loadMcpServers } from '../mcp/config.js';
|
|
@@ -29,7 +27,7 @@ import { SkillRepository } from '../skills/skillRepository.js';
|
|
|
29
27
|
import { createSkillTools } from '../tools/skillTools.js';
|
|
30
28
|
import { FileChangeTracker } from './fileChangeTracker.js';
|
|
31
29
|
import { formatShortcutsHelp } from '../ui/shortcutsHelp.js';
|
|
32
|
-
import { setPlanApprovalCallback
|
|
30
|
+
import { setPlanApprovalCallback } from '../tools/planningTools.js';
|
|
33
31
|
import { MetricsTracker } from '../core/metricsTracker.js';
|
|
34
32
|
import { detectFailure, clearActionHistory, findRecoveryStrategy, } from '../core/failureRecovery.js';
|
|
35
33
|
import { addToolPattern } from '../core/learningPersistence.js';
|
|
@@ -42,7 +40,6 @@ import { startOffsecRun, resumeOffsecRun, recordOffsecOutcome, getOffsecNextActi
|
|
|
42
40
|
import { generateTestFlows, detectBugs, detectUIUpdates, saveTestFlows, saveBugReports, saveUIUpdates, getTestFlowStatus, } from '../core/intelligentTestFlows.js';
|
|
43
41
|
import { PromptController } from '../ui/PromptController.js';
|
|
44
42
|
import { enterStreamingMode, exitStreamingMode, isStreamingMode } from '../ui/globalWriteLock.js';
|
|
45
|
-
import { PlanOverlay } from '../ui/planOverlay.js';
|
|
46
43
|
import { setGlobalAIEnhancer } from '../tools/localExplore.js';
|
|
47
44
|
import { createProvider } from '../providers/providerFactory.js';
|
|
48
45
|
import { getParallelAgentManager } from '../subagents/parallelAgentManager.js';
|
|
@@ -132,14 +129,11 @@ export class InteractiveShell {
|
|
|
132
129
|
activeContextWindowTokens = null;
|
|
133
130
|
latestTokenUsage = { used: null, limit: null };
|
|
134
131
|
planApprovalBridgeRegistered = false;
|
|
135
|
-
planUpdateBridgeRegistered = false;
|
|
136
|
-
planOverlay = new PlanOverlay();
|
|
137
132
|
contextCompactionInFlight = false;
|
|
138
133
|
contextCompactionLog = [];
|
|
139
134
|
lastContextWarningLevel = null;
|
|
140
135
|
sessionPreferences;
|
|
141
136
|
autosaveEnabled;
|
|
142
|
-
autoContinueEnabled;
|
|
143
137
|
verificationEnabled = false;
|
|
144
138
|
criticalApprovalMode = 'auto';
|
|
145
139
|
editGuardMode = 'display-edits';
|
|
@@ -185,7 +179,6 @@ export class InteractiveShell {
|
|
|
185
179
|
streamingOutputSuppressed = false;
|
|
186
180
|
aiRuntimeStart = null;
|
|
187
181
|
aiRuntimeTotalMs = 0;
|
|
188
|
-
streamingFormatter = null;
|
|
189
182
|
streamingThoughtBuffer = '';
|
|
190
183
|
statusLineState = null;
|
|
191
184
|
statusMessageOverride = null;
|
|
@@ -210,7 +203,6 @@ export class InteractiveShell {
|
|
|
210
203
|
this.sessionPreferences = loadSessionPreferences();
|
|
211
204
|
this.thinkingMode = this.sessionPreferences.thinkingMode;
|
|
212
205
|
this.autosaveEnabled = this.sessionPreferences.autosave;
|
|
213
|
-
this.autoContinueEnabled = this.sessionPreferences.autoContinue;
|
|
214
206
|
this.criticalApprovalMode = this.sessionPreferences.criticalApprovalMode;
|
|
215
207
|
const featureFlags = loadFeatureFlags();
|
|
216
208
|
this.verificationEnabled = featureFlags.verification === true;
|
|
@@ -269,11 +261,6 @@ export class InteractiveShell {
|
|
|
269
261
|
description: 'Switch between auto and approval mode for high-impact actions',
|
|
270
262
|
category: 'configuration',
|
|
271
263
|
});
|
|
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
|
-
});
|
|
277
264
|
this.slashCommands.push({
|
|
278
265
|
command: '/offsec',
|
|
279
266
|
description: 'AlphaZero offensive security run (start/status/next)',
|
|
@@ -292,6 +279,10 @@ export class InteractiveShell {
|
|
|
292
279
|
this.uiAdapter.setToolStatusCallback((status) => {
|
|
293
280
|
this.updateStatusMessage(status ?? null);
|
|
294
281
|
});
|
|
282
|
+
// Set up activity callback to update activity line with streaming bash output
|
|
283
|
+
this.uiAdapter.setActivityCallback((activity) => {
|
|
284
|
+
this.renderer?.setActivity(activity);
|
|
285
|
+
});
|
|
295
286
|
this.skillRepository = new SkillRepository({
|
|
296
287
|
workingDir: this.workingDir,
|
|
297
288
|
env: process.env,
|
|
@@ -305,20 +296,18 @@ export class InteractiveShell {
|
|
|
305
296
|
onQueue: (text) => this.handleQueuedInput(text),
|
|
306
297
|
onCtrlC: ({ hadBuffer }) => this.handleCtrlCPress(hadBuffer),
|
|
307
298
|
onInterrupt: () => this.handleInterrupt(),
|
|
308
|
-
onExit: () => this.
|
|
309
|
-
onExpandToolResult: () => display.expandLastToolResult?.(),
|
|
299
|
+
onExit: () => this.handleExit(),
|
|
310
300
|
onChange: ({ text, cursor }) => this.handleInputChange(text, cursor, 'renderer'),
|
|
311
301
|
onEditModeChange: (mode) => this.handleEditModeChange(mode),
|
|
312
302
|
onToggleVerify: () => this.toggleVerificationMode(),
|
|
313
|
-
onToggleAutoContinue: () => this.toggleAutoContinueMode(),
|
|
314
303
|
onToggleThinking: () => this.cycleThinkingMode(),
|
|
315
304
|
onClearContext: () => this.handleClearContext(),
|
|
316
305
|
onToggleCriticalApproval: () => this.toggleCriticalApprovalMode('shortcut'),
|
|
306
|
+
onExpandToolResult: () => this.expandLastToolResult(),
|
|
317
307
|
});
|
|
318
308
|
// Share renderer with Display so all output flows through the unified queue
|
|
319
309
|
this.renderer = this.terminalInput.getRenderer();
|
|
320
310
|
display.setRenderer(this.renderer);
|
|
321
|
-
this.uiAdapter.attachRenderer(this.renderer);
|
|
322
311
|
display.setInlinePanelHandler((content) => {
|
|
323
312
|
if (!this.shouldCaptureInlinePanel()) {
|
|
324
313
|
return false;
|
|
@@ -338,7 +327,6 @@ export class InteractiveShell {
|
|
|
338
327
|
// The control bar will be refreshed after the welcome banner
|
|
339
328
|
// Allow planning tools (e.g., ProposePlan) to open the interactive approval UI just like Codex CLI
|
|
340
329
|
this.registerPlanApprovalBridge();
|
|
341
|
-
this.registerPlanUpdateBridge();
|
|
342
330
|
// Set up command autocomplete with all slash commands
|
|
343
331
|
this.setupCommandAutocomplete();
|
|
344
332
|
this.rebuildAgent();
|
|
@@ -459,18 +447,21 @@ export class InteractiveShell {
|
|
|
459
447
|
const top = `╭${'─'.repeat(labelLeft)}${label}${'─'.repeat(labelRight)}╮`;
|
|
460
448
|
const bottom = `╰${'─'.repeat(contentWidth)}╯`;
|
|
461
449
|
const userName = process.env['USER'] || 'there';
|
|
462
|
-
const logo =
|
|
450
|
+
const logo = '⟣╍◎╍⟢';
|
|
463
451
|
const versionLabel = version ? clamp(`${brand} · v${version}`) : brand;
|
|
464
452
|
const modelLine = clamp(model);
|
|
465
453
|
const workspaceLine = clamp(workspace);
|
|
466
|
-
|
|
454
|
+
// Compact welcome line with logo on same line
|
|
455
|
+
const welcomeLine = clamp(`${logo} Welcome back ${userName}! ${logo}`);
|
|
456
|
+
// Quick reference hints
|
|
457
|
+
const hintsLine = clamp('/model · /secrets · /help · ? shortcuts');
|
|
467
458
|
const lines = [
|
|
468
459
|
frame(top),
|
|
469
460
|
frame(`│${padCenter(welcomeLine, contentWidth)}│`),
|
|
470
|
-
frame(`│${padCenter(logo, contentWidth)}│`),
|
|
471
461
|
frame(`│${padCenter(modelLine, contentWidth)}│`),
|
|
472
462
|
frame(`│${padCenter(versionLabel, contentWidth)}│`),
|
|
473
463
|
frame(`│${padCenter(workspaceLine, contentWidth)}│`),
|
|
464
|
+
frame(`│${padCenter(hintsLine, contentWidth)}│`),
|
|
474
465
|
frame(bottom),
|
|
475
466
|
].join('\n');
|
|
476
467
|
if (this.renderer) {
|
|
@@ -481,8 +472,6 @@ export class InteractiveShell {
|
|
|
481
472
|
else {
|
|
482
473
|
display.stream(`\n${lines}\n`);
|
|
483
474
|
}
|
|
484
|
-
// Check for updates asynchronously (non-blocking)
|
|
485
|
-
void this.checkAndShowUpdates();
|
|
486
475
|
// Keep UI pinned; no scrollback banners
|
|
487
476
|
this.requestPromptRefresh(true);
|
|
488
477
|
}
|
|
@@ -520,22 +509,32 @@ export class InteractiveShell {
|
|
|
520
509
|
}
|
|
521
510
|
async checkAndShowUpdates() {
|
|
522
511
|
try {
|
|
523
|
-
const { checkForUpdates,
|
|
512
|
+
const { checkForUpdates, getUpdateDecision, formatUpdateBanner, performBackgroundUpdate, readAutoUpdateState, shouldShowUpdateNotification, } = await import('../core/updateChecker.js');
|
|
524
513
|
const currentVersion = this.version || this.getPackageInfo().version || '1.7.458';
|
|
525
514
|
const updateInfo = await checkForUpdates(currentVersion);
|
|
526
515
|
if (!updateInfo || !updateInfo.updateAvailable) {
|
|
527
516
|
return;
|
|
528
517
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
518
|
+
// Check if we should show notification (respects quiet period)
|
|
519
|
+
const state = readAutoUpdateState();
|
|
520
|
+
if (!shouldShowUpdateNotification(state)) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
// Get user's preference-based decision (no interactive prompt)
|
|
524
|
+
const sessionPrefs = loadSessionPreferences();
|
|
525
|
+
const decision = getUpdateDecision(sessionPrefs.autoUpdate);
|
|
526
|
+
if (decision === 'skip') {
|
|
527
|
+
// User chose to always skip - silently ignore updates
|
|
534
528
|
return;
|
|
535
529
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
530
|
+
// Show non-interactive banner notification
|
|
531
|
+
this.streamEventBlock(formatUpdateBanner(updateInfo, decision));
|
|
532
|
+
if (decision === 'auto') {
|
|
533
|
+
// User chose to always update - perform background update
|
|
534
|
+
await performBackgroundUpdate(updateInfo, (message) => this.streamEventBlock(message));
|
|
535
|
+
}
|
|
536
|
+
// If decision === 'ask', we just show the banner with instructions
|
|
537
|
+
// User can run /update check or /update auto to update
|
|
539
538
|
}
|
|
540
539
|
catch {
|
|
541
540
|
// Silently fail - don't interrupt user experience
|
|
@@ -619,6 +618,8 @@ export class InteractiveShell {
|
|
|
619
618
|
this.pushUiEvent('raw', block);
|
|
620
619
|
}
|
|
621
620
|
async start(initialPrompt) {
|
|
621
|
+
// Check for updates BEFORE terminal input starts (to avoid keypress conflicts)
|
|
622
|
+
await this.checkAndShowUpdates();
|
|
622
623
|
// Initialize the renderer before emitting the banner so we don't render the prompt twice
|
|
623
624
|
this.terminalInput.start();
|
|
624
625
|
this.resetRendererStreamingMode();
|
|
@@ -627,8 +628,9 @@ export class InteractiveShell {
|
|
|
627
628
|
this.refreshControlBar();
|
|
628
629
|
// Now sync renderer and control bar state
|
|
629
630
|
this.syncRendererInput();
|
|
630
|
-
//
|
|
631
|
-
|
|
631
|
+
// CRITICAL: Force prompt render after banner - flushEvents should have done this,
|
|
632
|
+
// but explicitly ensure the prompt area is visible
|
|
633
|
+
this.renderer?.render();
|
|
632
634
|
if (initialPrompt) {
|
|
633
635
|
await this.enqueuePromptForProcessing(initialPrompt, 'programmatic');
|
|
634
636
|
return;
|
|
@@ -636,7 +638,7 @@ export class InteractiveShell {
|
|
|
636
638
|
this.showLaunchCommandPalette();
|
|
637
639
|
// Ensure the terminal input is visible
|
|
638
640
|
this.syncRendererInput();
|
|
639
|
-
this.
|
|
641
|
+
this.renderer?.render();
|
|
640
642
|
}
|
|
641
643
|
showLaunchCommandPalette() {
|
|
642
644
|
// Disabled: Quick commands palette takes up too much space
|
|
@@ -704,6 +706,10 @@ export class InteractiveShell {
|
|
|
704
706
|
*/
|
|
705
707
|
async runPromptJob(job) {
|
|
706
708
|
try {
|
|
709
|
+
// Show thinking indicator instead of "processing queued prompt"
|
|
710
|
+
display.showThinking('Thinking…');
|
|
711
|
+
// Re-echo the prompt to make it clear what's being processed
|
|
712
|
+
this.renderer?.emitPrompt(job.text);
|
|
707
713
|
await this.processInputBlock(job.text);
|
|
708
714
|
job.resolve();
|
|
709
715
|
}
|
|
@@ -799,7 +805,6 @@ export class InteractiveShell {
|
|
|
799
805
|
'/output-style',
|
|
800
806
|
// Mode toggles
|
|
801
807
|
'/thinking',
|
|
802
|
-
'/autocontinue',
|
|
803
808
|
// Discovery and plugins
|
|
804
809
|
'/local', '/discover',
|
|
805
810
|
'/plugins',
|
|
@@ -889,25 +894,22 @@ export class InteractiveShell {
|
|
|
889
894
|
: '🛡️ High-impact actions now require your approval. (Ctrl+Shift+A to toggle; use /approvals ask to persist)';
|
|
890
895
|
display.showSystemMessage(message);
|
|
891
896
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
this.agent.setAutoContinue(this.autoContinueEnabled);
|
|
901
|
-
}
|
|
902
|
-
this.refreshControlBar();
|
|
903
|
-
if (!changed && source === 'shortcut') {
|
|
897
|
+
/**
|
|
898
|
+
* Expand the last tool result (Ctrl+O shortcut).
|
|
899
|
+
* Shows the full output from the most recent tool call.
|
|
900
|
+
*/
|
|
901
|
+
expandLastToolResult() {
|
|
902
|
+
const result = this.uiAdapter.getLastToolResult();
|
|
903
|
+
if (!result) {
|
|
904
|
+
display.showInfo('No tool result to expand. Run a tool first, then press Ctrl+O.');
|
|
904
905
|
return;
|
|
905
906
|
}
|
|
906
|
-
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
907
|
+
// Format the expanded output with tool name header
|
|
908
|
+
const header = `${theme.info('━━━')} ${theme.tool(result.toolName)} ${theme.ui.muted('output')} ${theme.info('━━━')}`;
|
|
909
|
+
const content = result.output.trim() || '(empty result)';
|
|
910
|
+
const footer = theme.ui.muted('━'.repeat(Math.min(60, header.length)));
|
|
911
|
+
// Display the expanded result
|
|
912
|
+
display.stream(`\n${header}\n${content}\n${footer}\n\n`);
|
|
911
913
|
}
|
|
912
914
|
/**
|
|
913
915
|
* Cycle through thinking modes (Tab shortcut).
|
|
@@ -1061,6 +1063,13 @@ export class InteractiveShell {
|
|
|
1061
1063
|
}
|
|
1062
1064
|
this.ctrlCHandledThisPress = false;
|
|
1063
1065
|
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Handle exit request (Ctrl+C with empty buffer in idle mode)
|
|
1068
|
+
*/
|
|
1069
|
+
handleExit() {
|
|
1070
|
+
display.showSystemMessage('\nGoodbye!\n');
|
|
1071
|
+
this.shutdown();
|
|
1072
|
+
}
|
|
1064
1073
|
/**
|
|
1065
1074
|
* Gracefully tear down the shell and exit
|
|
1066
1075
|
*/
|
|
@@ -1070,7 +1079,7 @@ export class InteractiveShell {
|
|
|
1070
1079
|
}
|
|
1071
1080
|
this.shuttingDown = true;
|
|
1072
1081
|
// Stop any active spinner to prevent process hang
|
|
1073
|
-
display.stopThinking();
|
|
1082
|
+
display.stopThinking(false);
|
|
1074
1083
|
this.stopStreamingHeartbeat('quit', { quiet: true });
|
|
1075
1084
|
this.endAiRuntime();
|
|
1076
1085
|
this.uiUpdates.dispose();
|
|
@@ -1082,7 +1091,6 @@ export class InteractiveShell {
|
|
|
1082
1091
|
this.pendingCleanup = null;
|
|
1083
1092
|
// Unregister plan approval bridge
|
|
1084
1093
|
setPlanApprovalCallback(null);
|
|
1085
|
-
setPlanUpdateCallback(null);
|
|
1086
1094
|
// Dispose terminal input handler
|
|
1087
1095
|
this.terminalInput.dispose();
|
|
1088
1096
|
// Dispose unified UI adapter
|
|
@@ -1103,38 +1111,6 @@ export class InteractiveShell {
|
|
|
1103
1111
|
});
|
|
1104
1112
|
this.planApprovalBridgeRegistered = true;
|
|
1105
1113
|
}
|
|
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
|
-
}
|
|
1138
1114
|
/**
|
|
1139
1115
|
* Update status bar message
|
|
1140
1116
|
*/
|
|
@@ -1663,9 +1639,7 @@ export class InteractiveShell {
|
|
|
1663
1639
|
refreshControlBar() {
|
|
1664
1640
|
this.terminalInput.setModeToggles({
|
|
1665
1641
|
verificationEnabled: this.verificationEnabled,
|
|
1666
|
-
autoContinueEnabled: this.autoContinueEnabled,
|
|
1667
1642
|
verificationHotkey: 'ctrl+shift+v',
|
|
1668
|
-
autoContinueHotkey: 'ctrl+shift+c',
|
|
1669
1643
|
thinkingModeLabel: (this.thinkingMode || 'off').toString(),
|
|
1670
1644
|
thinkingHotkey: 'tab',
|
|
1671
1645
|
criticalApprovalMode: this.criticalApprovalMode,
|
|
@@ -1756,15 +1730,14 @@ export class InteractiveShell {
|
|
|
1756
1730
|
*/
|
|
1757
1731
|
refreshStatusLine(forceRender = false) {
|
|
1758
1732
|
const elapsedSeconds = this.getAiRuntimeSeconds();
|
|
1759
|
-
const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
|
|
1760
1733
|
const tokensUsed = this.latestTokenUsage.used;
|
|
1761
1734
|
const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
|
|
1762
1735
|
this.terminalInput.setMetaStatus({
|
|
1763
1736
|
elapsedSeconds,
|
|
1764
1737
|
tokensUsed,
|
|
1765
1738
|
tokenLimit,
|
|
1766
|
-
thinkingMs,
|
|
1767
|
-
thinkingHasContent:
|
|
1739
|
+
thinkingMs: null,
|
|
1740
|
+
thinkingHasContent: false,
|
|
1768
1741
|
});
|
|
1769
1742
|
// Keep model/provider visible in the controls bar
|
|
1770
1743
|
this.terminalInput.setModelContext({
|
|
@@ -1940,12 +1913,16 @@ export class InteractiveShell {
|
|
|
1940
1913
|
enterStreamingMode();
|
|
1941
1914
|
// Set up scroll region for streaming content
|
|
1942
1915
|
this.uiUpdates.setMode('streaming');
|
|
1916
|
+
// Show activity status with animated spinner - use provided label or default
|
|
1917
|
+
const activityLabel = label || 'Thinking';
|
|
1918
|
+
this.renderer?.setActivity(activityLabel);
|
|
1943
1919
|
this.streamingHeartbeatStart = Date.now();
|
|
1944
1920
|
this.streamingContentSeen = false;
|
|
1945
1921
|
this.streamingStatusText = null;
|
|
1946
1922
|
this.streamingStatusLastUpdate = null;
|
|
1947
|
-
|
|
1948
|
-
|
|
1923
|
+
this.streamingTokenCount = 0; // Reset token count for new prompt
|
|
1924
|
+
// Show raw streaming output in real-time (Claude Code style)
|
|
1925
|
+
this.streamingOutputSuppressed = false;
|
|
1949
1926
|
const initialLabel = this.isMeaningfulStreamingSnippet(label)
|
|
1950
1927
|
? this.truncateStreamingLabel(label)
|
|
1951
1928
|
: this.getStreamingFallbackLabel();
|
|
@@ -1989,6 +1966,8 @@ export class InteractiveShell {
|
|
|
1989
1966
|
this.streamingStatusLastUpdate = null;
|
|
1990
1967
|
this.streamingStatusText = null;
|
|
1991
1968
|
this.streamingOutputSuppressed = false;
|
|
1969
|
+
// Clear activity status when streaming ends
|
|
1970
|
+
this.renderer?.setActivity(null);
|
|
1992
1971
|
// Emit a streaming note for stop/quit so the status stays inside the stream
|
|
1993
1972
|
if (reason === 'stop' || reason === 'quit') {
|
|
1994
1973
|
const note = reason === 'quit' ? 'Session closed.' : 'Stream stopped.';
|
|
@@ -2005,11 +1984,15 @@ export class InteractiveShell {
|
|
|
2005
1984
|
// Buffer for accumulating partial <thinking> tags during streaming
|
|
2006
1985
|
thinkingTagBuffer = '';
|
|
2007
1986
|
insideThinkingBlock = false;
|
|
1987
|
+
streamingTokenCount = 0;
|
|
2008
1988
|
handleStreamChunk(chunk, type = 'content') {
|
|
2009
1989
|
if (!chunk) {
|
|
2010
1990
|
return;
|
|
2011
1991
|
}
|
|
2012
1992
|
const isReasoning = type === 'reasoning';
|
|
1993
|
+
// Approximate token count (roughly 4 chars per token)
|
|
1994
|
+
this.streamingTokenCount += Math.ceil(chunk.length / 4);
|
|
1995
|
+
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
2013
1996
|
// Keep pinned status updated for all streaming chunks
|
|
2014
1997
|
this.updateStreamingStatusFromChunk(chunk);
|
|
2015
1998
|
// Handle <thinking> tags as separate events in the queue
|
|
@@ -2018,37 +2001,32 @@ export class InteractiveShell {
|
|
|
2018
2001
|
if (!processed.contentChunk && !processed.thinkingChunk) {
|
|
2019
2002
|
return;
|
|
2020
2003
|
}
|
|
2021
|
-
//
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
}
|
|
2004
|
+
// Don't emit thinking blocks during streaming - they will be rendered
|
|
2005
|
+
// as a complete block by onAssistantMessage when the response is final.
|
|
2006
|
+
// This prevents duplicate display.
|
|
2025
2007
|
// Process regular content (skip if no content after extracting thinking)
|
|
2026
2008
|
const contentChunk = processed.contentChunk;
|
|
2027
2009
|
if (!contentChunk) {
|
|
2028
2010
|
return;
|
|
2029
2011
|
}
|
|
2030
2012
|
// Suppress raw streaming output in scrollback; keep only status + final message.
|
|
2031
|
-
// Reasoning
|
|
2032
|
-
if (this.streamingOutputSuppressed
|
|
2013
|
+
// Reasoning tokens should also be suppressed and shown only in the indicator.
|
|
2014
|
+
if (this.streamingOutputSuppressed) {
|
|
2033
2015
|
this.captureStreamingThought(contentChunk);
|
|
2034
2016
|
this.streamingContentSeen = true;
|
|
2017
|
+
// Update thinking indicator with a snippet of the content
|
|
2018
|
+
const prefix = isReasoning ? '○ ' : '';
|
|
2019
|
+
const snippet = contentChunk.replace(/\s+/g, ' ').trim().slice(0, 60);
|
|
2020
|
+
if (snippet) {
|
|
2021
|
+
display.updateThinking(prefix + snippet + (contentChunk.length > 60 ? '…' : ''));
|
|
2022
|
+
}
|
|
2035
2023
|
return;
|
|
2036
2024
|
}
|
|
2037
|
-
|
|
2038
|
-
|
|
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);
|
|
2025
|
+
// Buffer all streaming content - rendered as complete block via onAssistantMessage
|
|
2026
|
+
// This prevents fragmented display and ensures thinking tags are properly processed
|
|
2045
2027
|
this.captureStreamingThought(contentChunk);
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
this.streamingContentSeen = true;
|
|
2049
|
-
}
|
|
2050
|
-
this.pushUiEvent('streaming', formatted);
|
|
2051
|
-
}
|
|
2028
|
+
// Activity: show what the model is doing (stable, informative)
|
|
2029
|
+
this.renderer?.setActivity(type === 'reasoning' ? 'Reasoning' : 'Writing');
|
|
2052
2030
|
}
|
|
2053
2031
|
/**
|
|
2054
2032
|
* Process streaming content to extract <thinking> blocks as separate events.
|
|
@@ -2096,22 +2074,12 @@ export class InteractiveShell {
|
|
|
2096
2074
|
}
|
|
2097
2075
|
return { thinkingChunk: thinkingContent, contentChunk: remainingContent };
|
|
2098
2076
|
}
|
|
2099
|
-
finishStreamingFormatter(
|
|
2100
|
-
|
|
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
|
-
}
|
|
2077
|
+
finishStreamingFormatter(_note, options) {
|
|
2078
|
+
// Flush any remaining buffered thoughts
|
|
2110
2079
|
if (this.streamingThoughtBuffer.trim()) {
|
|
2111
2080
|
this.ui.controller.recordAssistantThought(this.streamingThoughtBuffer.trim());
|
|
2112
2081
|
}
|
|
2113
2082
|
this.streamingThoughtBuffer = '';
|
|
2114
|
-
this.streamingFormatter = null;
|
|
2115
2083
|
if (options?.refreshPrompt ?? true) {
|
|
2116
2084
|
this.requestPromptRefresh(true);
|
|
2117
2085
|
}
|
|
@@ -2403,9 +2371,6 @@ export class InteractiveShell {
|
|
|
2403
2371
|
case '/approvals':
|
|
2404
2372
|
this.handleApprovalsCommand(input);
|
|
2405
2373
|
break;
|
|
2406
|
-
case '/plan':
|
|
2407
|
-
this.handlePlanCommand(input);
|
|
2408
|
-
break;
|
|
2409
2374
|
case '/learn':
|
|
2410
2375
|
this.showLearningStatus(input);
|
|
2411
2376
|
break;
|
|
@@ -2453,9 +2418,6 @@ export class InteractiveShell {
|
|
|
2453
2418
|
case '/thinking':
|
|
2454
2419
|
this.handleThinkingCommand(input);
|
|
2455
2420
|
break;
|
|
2456
|
-
case '/autocontinue':
|
|
2457
|
-
this.handleAutoContinueCommand(input);
|
|
2458
|
-
break;
|
|
2459
2421
|
case '/shortcuts':
|
|
2460
2422
|
case '/keys':
|
|
2461
2423
|
this.handleShortcutsCommand();
|
|
@@ -2559,6 +2521,9 @@ export class InteractiveShell {
|
|
|
2559
2521
|
case '/permissions':
|
|
2560
2522
|
this.handlePermissionsCommand();
|
|
2561
2523
|
break;
|
|
2524
|
+
case '/update':
|
|
2525
|
+
await this.handleUpdateCommand(input);
|
|
2526
|
+
break;
|
|
2562
2527
|
case '/init':
|
|
2563
2528
|
this.handleInitCommand(input);
|
|
2564
2529
|
break;
|
|
@@ -2616,7 +2581,6 @@ export class InteractiveShell {
|
|
|
2616
2581
|
theme.bold(' Mode Toggles'),
|
|
2617
2582
|
` ${theme.info('Shift+Tab')} ${theme.ui.muted('Toggle edit mode (auto/ask)')}`,
|
|
2618
2583
|
` ${theme.info('Option+V')} ${theme.ui.muted('Toggle verification')}`,
|
|
2619
|
-
` ${theme.info('Option+C')} ${theme.ui.muted('Toggle auto-continue')}`,
|
|
2620
2584
|
` ${theme.info('Ctrl+Shift+A')} ${theme.ui.muted('Toggle approvals for high-impact actions')}`,
|
|
2621
2585
|
` ${theme.info('Option+T')} ${theme.ui.muted('Cycle thinking mode')}`,
|
|
2622
2586
|
` ${theme.info('Option+E')} ${theme.ui.muted('Toggle edit permission mode')}`,
|
|
@@ -4330,20 +4294,6 @@ export class InteractiveShell {
|
|
|
4330
4294
|
clearAutosaveSnapshot(this.profile);
|
|
4331
4295
|
display.showInfo('Cleared autosave history.');
|
|
4332
4296
|
}
|
|
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
|
-
}
|
|
4347
4297
|
// ==================== Erosolar-CLI Style Commands ====================
|
|
4348
4298
|
async handleRewindCommand(_input) {
|
|
4349
4299
|
const lines = [];
|
|
@@ -4575,7 +4525,6 @@ export class InteractiveShell {
|
|
|
4575
4525
|
lines.push(`${theme.primary('Provider/Model')}: ${theme.info(`${this.providerLabel(this.sessionState.provider)} · ${this.sessionState.model}`)}`);
|
|
4576
4526
|
lines.push(`${theme.primary('Workspace')}: ${theme.ui.muted(this.abbreviatePath(this.workingDir))}`);
|
|
4577
4527
|
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')}`);
|
|
4579
4528
|
lines.push(`${theme.primary('Verification')}: ${this.verificationEnabled ? theme.success('on') : theme.ui.muted('off')}`);
|
|
4580
4529
|
lines.push(`${theme.primary('Approvals')}: ${theme.ui.muted(this.describeEditGuardMode())}`);
|
|
4581
4530
|
lines.push(`${theme.primary('Critical approvals')}: ${this.criticalApprovalMode === 'approval' ? theme.warning('ask') : theme.ui.muted('auto')}`);
|
|
@@ -4711,52 +4660,6 @@ export class InteractiveShell {
|
|
|
4711
4660
|
}
|
|
4712
4661
|
display.showWarning('Usage: /approvals [auto|ask|status]');
|
|
4713
4662
|
}
|
|
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
|
-
}
|
|
4760
4663
|
handlePermissionsCommand() {
|
|
4761
4664
|
const lines = [];
|
|
4762
4665
|
lines.push(theme.bold('Tool Permissions'));
|
|
@@ -4777,6 +4680,67 @@ export class InteractiveShell {
|
|
|
4777
4680
|
lines.push(theme.ui.muted('Use /tools to manage which tool suites are enabled.'));
|
|
4778
4681
|
display.showSystemMessage(lines.join('\n'));
|
|
4779
4682
|
}
|
|
4683
|
+
async handleUpdateCommand(input) {
|
|
4684
|
+
const tokens = input.split(/\s+/).slice(1);
|
|
4685
|
+
const subcommand = tokens[0]?.toLowerCase();
|
|
4686
|
+
const prefs = loadSessionPreferences();
|
|
4687
|
+
const currentPref = prefs.autoUpdate;
|
|
4688
|
+
const prefLabel = currentPref === true ? 'always update' : currentPref === false ? 'always skip' : 'notify only';
|
|
4689
|
+
// Show status or help
|
|
4690
|
+
if (!subcommand || subcommand === 'status') {
|
|
4691
|
+
const lines = [];
|
|
4692
|
+
lines.push(theme.bold('Update Settings'));
|
|
4693
|
+
lines.push('');
|
|
4694
|
+
lines.push(`Current preference: ${theme.info(prefLabel)}`);
|
|
4695
|
+
lines.push('');
|
|
4696
|
+
lines.push(theme.secondary('Commands:'));
|
|
4697
|
+
lines.push(' /update check - Check for updates and install immediately');
|
|
4698
|
+
lines.push(' /update auto - Always auto-update in background');
|
|
4699
|
+
lines.push(' /update skip - Never auto-update (silent)');
|
|
4700
|
+
lines.push(' /update notify - Show notification only (default)');
|
|
4701
|
+
display.showSystemMessage(lines.join('\n'));
|
|
4702
|
+
return;
|
|
4703
|
+
}
|
|
4704
|
+
if (subcommand === 'check') {
|
|
4705
|
+
// Force check and perform update immediately (non-interactive)
|
|
4706
|
+
try {
|
|
4707
|
+
const { checkForUpdates, performUpdate } = await import('../core/updateChecker.js');
|
|
4708
|
+
const currentVersion = this.version || this.getPackageInfo().version || '1.7.458';
|
|
4709
|
+
const updateInfo = await checkForUpdates(currentVersion);
|
|
4710
|
+
if (!updateInfo) {
|
|
4711
|
+
display.showWarning('Unable to check for updates (network issue or timeout).');
|
|
4712
|
+
return;
|
|
4713
|
+
}
|
|
4714
|
+
if (!updateInfo.updateAvailable) {
|
|
4715
|
+
display.showSuccess(`You're on the latest version (v${updateInfo.current}).`);
|
|
4716
|
+
return;
|
|
4717
|
+
}
|
|
4718
|
+
// Show update info and perform update immediately (no interactive prompt)
|
|
4719
|
+
display.showInfo(`Update available: v${updateInfo.current} → v${updateInfo.latest}`);
|
|
4720
|
+
await performUpdate(updateInfo, (msg) => this.streamEventBlock(msg));
|
|
4721
|
+
}
|
|
4722
|
+
catch (error) {
|
|
4723
|
+
display.showError(`Update check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
4724
|
+
}
|
|
4725
|
+
return;
|
|
4726
|
+
}
|
|
4727
|
+
if (subcommand === 'auto' || subcommand === 'always') {
|
|
4728
|
+
saveSessionPreferences({ autoUpdate: true });
|
|
4729
|
+
display.showSuccess('Auto-update enabled. Updates will install automatically.');
|
|
4730
|
+
return;
|
|
4731
|
+
}
|
|
4732
|
+
if (subcommand === 'skip' || subcommand === 'never' || subcommand === 'off') {
|
|
4733
|
+
saveSessionPreferences({ autoUpdate: false });
|
|
4734
|
+
display.showInfo('Auto-update disabled. Updates will be skipped silently.');
|
|
4735
|
+
return;
|
|
4736
|
+
}
|
|
4737
|
+
if (subcommand === 'ask' || subcommand === 'prompt' || subcommand === 'reset' || subcommand === 'notify') {
|
|
4738
|
+
saveSessionPreferences({ autoUpdate: null });
|
|
4739
|
+
display.showInfo('Update preference reset. You will see a notification when updates are available.');
|
|
4740
|
+
return;
|
|
4741
|
+
}
|
|
4742
|
+
display.showWarning('Usage: /update [check|auto|skip|notify|status]');
|
|
4743
|
+
}
|
|
4780
4744
|
handleInitCommand(input) {
|
|
4781
4745
|
const tokens = input.split(/\s+/).slice(1);
|
|
4782
4746
|
const confirm = tokens[0]?.toLowerCase() === 'confirm';
|
|
@@ -5801,6 +5765,7 @@ export class InteractiveShell {
|
|
|
5801
5765
|
}
|
|
5802
5766
|
this.isProcessing = true;
|
|
5803
5767
|
this.uiUpdates.setMode('processing');
|
|
5768
|
+
this.streamingTokenCount = 0; // Reset token counter for new request
|
|
5804
5769
|
this.terminalInput.setStreaming(true);
|
|
5805
5770
|
// Keep the persistent input/control bar active as we transition into streaming.
|
|
5806
5771
|
this.syncRendererInput();
|
|
@@ -5815,6 +5780,7 @@ export class InteractiveShell {
|
|
|
5815
5780
|
this.currentTaskType = classifyTaskType(request);
|
|
5816
5781
|
this.currentToolCalls = [];
|
|
5817
5782
|
this.clearToolUsageMeta();
|
|
5783
|
+
this.renderer?.setActivity('Starting...');
|
|
5818
5784
|
this.uiAdapter.startProcessing('Working on your request');
|
|
5819
5785
|
this.setProcessingStatus();
|
|
5820
5786
|
this.beginAiRuntime();
|
|
@@ -5870,8 +5836,6 @@ export class InteractiveShell {
|
|
|
5870
5836
|
clearActionHistory();
|
|
5871
5837
|
this.lastFailure = null;
|
|
5872
5838
|
}
|
|
5873
|
-
// Run post-hoc AI flow sanity check to catch "looks right but wrong" responses
|
|
5874
|
-
this.analyzeAiFlowForRun(request, responseText, requestStartTime);
|
|
5875
5839
|
}
|
|
5876
5840
|
catch (error) {
|
|
5877
5841
|
const handled = this.handleProviderError(error, () => this.processRequest(request));
|
|
@@ -5891,7 +5855,7 @@ export class InteractiveShell {
|
|
|
5891
5855
|
this.responseRendered = true;
|
|
5892
5856
|
}
|
|
5893
5857
|
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5894
|
-
display.stopThinking();
|
|
5858
|
+
display.stopThinking(false);
|
|
5895
5859
|
this.uiUpdates.setMode('processing');
|
|
5896
5860
|
this.stopStreamingHeartbeat('complete', { quiet: true });
|
|
5897
5861
|
this.endAiRuntime();
|
|
@@ -5942,6 +5906,7 @@ export class InteractiveShell {
|
|
|
5942
5906
|
this.clearToolUsageMeta();
|
|
5943
5907
|
this.isProcessing = true;
|
|
5944
5908
|
this.uiUpdates.setMode('processing');
|
|
5909
|
+
this.streamingTokenCount = 0; // Reset token counter for new request
|
|
5945
5910
|
this.terminalInput.setStreaming(true);
|
|
5946
5911
|
if (this.suppressNextNetworkReset) {
|
|
5947
5912
|
this.suppressNextNetworkReset = false;
|
|
@@ -6107,7 +6072,7 @@ What's the next action?`;
|
|
|
6107
6072
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
6108
6073
|
}
|
|
6109
6074
|
catch (error) {
|
|
6110
|
-
display.stopThinking();
|
|
6075
|
+
display.stopThinking(false);
|
|
6111
6076
|
// Handle context overflow specially - the agent should auto-recover
|
|
6112
6077
|
// but if it propagates here, we continue the loop
|
|
6113
6078
|
if (this.isContextOverflowError(error)) {
|
|
@@ -6349,43 +6314,6 @@ What's the next action?`;
|
|
|
6349
6314
|
const parts = candidates.filter((part) => typeof part === 'string' && part.trim().length > 0);
|
|
6350
6315
|
return parts.join('\n').trim();
|
|
6351
6316
|
}
|
|
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
|
-
}
|
|
6389
6317
|
runAutoQualityChecks(trigger, assistantResponse, verificationContext) {
|
|
6390
6318
|
if (!this.verificationEnabled) {
|
|
6391
6319
|
return;
|
|
@@ -6766,21 +6694,41 @@ Return ONLY JSON array:
|
|
|
6766
6694
|
maxTokens: this.sessionState.maxTokens,
|
|
6767
6695
|
systemPrompt: this.buildSystemPrompt(),
|
|
6768
6696
|
reasoningEffort: this.sessionState.reasoningEffort,
|
|
6769
|
-
autoContinue: this.autoContinueEnabled,
|
|
6770
6697
|
};
|
|
6771
6698
|
this.agent = this.runtimeSession.createAgent(selection, {
|
|
6699
|
+
onRequestReceived: (requestPreview) => {
|
|
6700
|
+
const normalized = requestPreview?.trim();
|
|
6701
|
+
const activity = normalized ? `Working: ${normalized}` : 'Working';
|
|
6702
|
+
this.renderer?.setActivity(activity);
|
|
6703
|
+
},
|
|
6704
|
+
onBeforeFirstToolCall: (toolNames) => {
|
|
6705
|
+
const primaryTool = toolNames[0];
|
|
6706
|
+
if (primaryTool) {
|
|
6707
|
+
this.renderer?.setActivity(`Running ${primaryTool}`);
|
|
6708
|
+
}
|
|
6709
|
+
return undefined;
|
|
6710
|
+
},
|
|
6772
6711
|
onStreamChunk: (chunk, type) => {
|
|
6773
6712
|
this.handleStreamChunk(chunk, type ?? 'content');
|
|
6774
6713
|
},
|
|
6775
|
-
onStreamFallback: (info) => this.handleStreamingFallback(info),
|
|
6776
6714
|
onAssistantMessage: (content, metadata) => {
|
|
6777
6715
|
const enriched = this.buildDisplayMetadata(metadata);
|
|
6778
6716
|
const streamedVisible = metadata.wasStreamed && this.streamingContentSeen && !this.streamingOutputSuppressed;
|
|
6779
6717
|
let renderedFinal = false;
|
|
6718
|
+
// Update streaming token count from usage info (more accurate than chunk counting)
|
|
6719
|
+
if (metadata.usage) {
|
|
6720
|
+
const totalTokens = this.totalTokens(metadata.usage);
|
|
6721
|
+
if (totalTokens !== null && totalTokens > this.streamingTokenCount) {
|
|
6722
|
+
this.streamingTokenCount = totalTokens;
|
|
6723
|
+
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6724
|
+
}
|
|
6725
|
+
}
|
|
6780
6726
|
// Update spinner based on message type
|
|
6781
6727
|
if (metadata.isFinal) {
|
|
6782
6728
|
const parsed = this.splitThinkingResponse(content);
|
|
6783
|
-
|
|
6729
|
+
// If we successfully parsed thinking, use the parsed response (may be empty)
|
|
6730
|
+
// Don't fall back to original content with raw <thinking> tags
|
|
6731
|
+
const finalContent = parsed ? parsed.response?.trim() : content;
|
|
6784
6732
|
const thoughtContent = parsed?.thinking?.trim() || null;
|
|
6785
6733
|
// Show the response if it wasn't already rendered during streaming
|
|
6786
6734
|
if (!streamedVisible) {
|
|
@@ -6883,12 +6831,6 @@ Return ONLY JSON array:
|
|
|
6883
6831
|
this.updateStatusMessage('Retrying with reduced context...');
|
|
6884
6832
|
this.syncRendererInput();
|
|
6885
6833
|
},
|
|
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
|
-
},
|
|
6892
6834
|
onCancelled: () => {
|
|
6893
6835
|
// Update UI to show operation was cancelled
|
|
6894
6836
|
display.showWarning('Operation cancelled.');
|
|
@@ -6897,10 +6839,42 @@ Return ONLY JSON array:
|
|
|
6897
6839
|
this.updateStatusMessage(null);
|
|
6898
6840
|
this.terminalInput.setStreaming(false);
|
|
6899
6841
|
},
|
|
6842
|
+
onToolExecution: (toolName, isStart, args) => {
|
|
6843
|
+
// Update activity status to show what tool is being executed
|
|
6844
|
+
if (isStart) {
|
|
6845
|
+
// Show more specific activity for long-running tools
|
|
6846
|
+
let activity = `Running ${toolName}`;
|
|
6847
|
+
if (toolName === 'execute_bash' && args?.['command']) {
|
|
6848
|
+
const cmd = String(args['command']).slice(0, 40);
|
|
6849
|
+
activity = `$ ${cmd}${String(args['command']).length > 40 ? '...' : ''}`;
|
|
6850
|
+
}
|
|
6851
|
+
else if (toolName === 'read_file' && args?.['file_path']) {
|
|
6852
|
+
const path = String(args['file_path']).split('/').pop() || args['file_path'];
|
|
6853
|
+
activity = `Reading ${path}`;
|
|
6854
|
+
}
|
|
6855
|
+
this.renderer?.setActivity(activity);
|
|
6856
|
+
// Estimate tokens for tool call (~50 tokens per call)
|
|
6857
|
+
this.streamingTokenCount += 50;
|
|
6858
|
+
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6859
|
+
}
|
|
6860
|
+
else {
|
|
6861
|
+
// Tool finished - estimate result tokens (~100 per result)
|
|
6862
|
+
this.streamingTokenCount += 100;
|
|
6863
|
+
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6864
|
+
// Reset to thinking state while model generates next response
|
|
6865
|
+
this.renderer?.setActivity('Thinking');
|
|
6866
|
+
}
|
|
6867
|
+
},
|
|
6900
6868
|
onVerificationNeeded: (response, context) => {
|
|
6901
6869
|
this.lastAssistantResponse = response;
|
|
6902
6870
|
void this.runAutoQualityChecks('verification', response, context);
|
|
6903
6871
|
},
|
|
6872
|
+
// Retry notification for transient errors
|
|
6873
|
+
onRetrying: (attempt, maxAttempts, error) => {
|
|
6874
|
+
const shortError = error.message.slice(0, 100);
|
|
6875
|
+
display.showSystemMessage(`⚡ Retry ${attempt}/${maxAttempts}: ${shortError}${error.message.length > 100 ? '...' : ''}`);
|
|
6876
|
+
this.renderer?.setActivity(`Retrying (${attempt}/${maxAttempts})...`);
|
|
6877
|
+
},
|
|
6904
6878
|
});
|
|
6905
6879
|
// Register global AI enhancer for explore tool - uses active model by default
|
|
6906
6880
|
this.registerExploreAIEnhancer();
|
|
@@ -6995,20 +6969,30 @@ Return ONLY JSON array:
|
|
|
6995
6969
|
return lines.join('\n').trim();
|
|
6996
6970
|
}
|
|
6997
6971
|
buildThinkingDirective() {
|
|
6972
|
+
// Base requirement: ALWAYS think before acting (applies to all modes)
|
|
6973
|
+
const baseRequirement = [
|
|
6974
|
+
'CRITICAL: Before calling ANY tool, ALWAYS output a <thinking>...</thinking> block explaining:',
|
|
6975
|
+
'1. What you understand the user is asking',
|
|
6976
|
+
'2. Your approach/plan to solve it',
|
|
6977
|
+
'3. Which tools you will use and why',
|
|
6978
|
+
'This thinking block MUST appear before your first tool call in every response.',
|
|
6979
|
+
].join('\n');
|
|
6998
6980
|
switch (this.thinkingMode) {
|
|
6999
6981
|
case 'extended':
|
|
7000
6982
|
return [
|
|
7001
|
-
|
|
7002
|
-
'
|
|
7003
|
-
'
|
|
7004
|
-
'
|
|
7005
|
-
'<response
|
|
7006
|
-
'Final answer with actionable next steps and any code/commands requested.',
|
|
7007
|
-
'</response>',
|
|
6983
|
+
baseRequirement,
|
|
6984
|
+
'',
|
|
6985
|
+
'Extended thinking mode: Use detailed multi-step reasoning in your <thinking> block.',
|
|
6986
|
+
'Reference tool runs/files when relevant. Keep secrets redacted.',
|
|
6987
|
+
'After thinking, wrap your final answer in <response>...</response>.',
|
|
7008
6988
|
].join('\n');
|
|
7009
6989
|
case 'balanced':
|
|
7010
6990
|
default:
|
|
7011
|
-
return
|
|
6991
|
+
return [
|
|
6992
|
+
baseRequirement,
|
|
6993
|
+
'',
|
|
6994
|
+
'Balanced mode: Keep thinking concise (2-4 sentences) but always present.',
|
|
6995
|
+
].join('\n');
|
|
7012
6996
|
}
|
|
7013
6997
|
}
|
|
7014
6998
|
buildDisplayMetadata(metadata) {
|
|
@@ -7099,13 +7083,14 @@ Return ONLY JSON array:
|
|
|
7099
7083
|
});
|
|
7100
7084
|
cleanupOverlayActive = true;
|
|
7101
7085
|
const triggerReason = trigger?.reason ?? 'Context optimization';
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7086
|
+
// Claude Code style: Show "Compacting conversation..." with animation
|
|
7087
|
+
// The renderer handles the animated spinner display
|
|
7088
|
+
if (this.renderer) {
|
|
7089
|
+
this.renderer.showCompactingStatus('Compacting conversation… (esc to interrupt)');
|
|
7090
|
+
}
|
|
7091
|
+
else {
|
|
7092
|
+
display.showSystemMessage(`✻ Compacting conversation… (esc to interrupt)`);
|
|
7093
|
+
}
|
|
7109
7094
|
const result = await contextManager.intelligentCompact(history);
|
|
7110
7095
|
let afterStats = contextManager.getStats(result.compacted);
|
|
7111
7096
|
let appliedHistory = result.compacted;
|
|
@@ -7132,10 +7117,18 @@ Return ONLY JSON array:
|
|
|
7132
7117
|
}
|
|
7133
7118
|
}
|
|
7134
7119
|
if (!changed) {
|
|
7120
|
+
// Hide compacting status before showing info message
|
|
7121
|
+
if (this.renderer) {
|
|
7122
|
+
this.renderer.hideCompactingStatus();
|
|
7123
|
+
}
|
|
7135
7124
|
display.showInfo('Context compaction completed but no changes were applied.');
|
|
7136
7125
|
return;
|
|
7137
7126
|
}
|
|
7138
7127
|
if (!this.hasMeaningfulCompaction(changed, bestTokenSavings, bestPercentSavings, trigger?.forced)) {
|
|
7128
|
+
// Hide compacting status before showing info message
|
|
7129
|
+
if (this.renderer) {
|
|
7130
|
+
this.renderer.hideCompactingStatus();
|
|
7131
|
+
}
|
|
7139
7132
|
display.showInfo('Auto-compaction completed but did not meaningfully reduce context size. Keeping existing history.');
|
|
7140
7133
|
return;
|
|
7141
7134
|
}
|
|
@@ -7151,15 +7144,18 @@ Return ONLY JSON array:
|
|
|
7151
7144
|
this.updateContextUsage(newPercentUsed, CONTEXT_AUTOCOMPACT_PERCENT);
|
|
7152
7145
|
this.lastContextWarningLevel = this.getContextWarningLevel(newPercentUsed);
|
|
7153
7146
|
this.refreshStatusLine(true);
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7147
|
+
// Claude Code style: Show compaction complete with separator and ctrl+o hint
|
|
7148
|
+
// Stop the compacting animation first
|
|
7149
|
+
if (this.renderer) {
|
|
7150
|
+
this.renderer.hideCompactingStatus();
|
|
7151
|
+
}
|
|
7152
|
+
// Show the Claude Code style separator: ══ Conversation compacted · ctrl+o for history ═
|
|
7153
|
+
if (this.renderer) {
|
|
7154
|
+
this.renderer.addCompactBlock('', 'Conversation compacted · ctrl+o for history');
|
|
7155
|
+
}
|
|
7156
|
+
else {
|
|
7157
|
+
display.showSystemMessage('══ Conversation compacted · ctrl+o for history ═');
|
|
7158
|
+
}
|
|
7163
7159
|
this.recordContextCompaction({
|
|
7164
7160
|
timestamp: Date.now(),
|
|
7165
7161
|
source: trigger?.source ?? 'auto',
|
|
@@ -7173,12 +7169,20 @@ Return ONLY JSON array:
|
|
|
7173
7169
|
});
|
|
7174
7170
|
}
|
|
7175
7171
|
catch (error) {
|
|
7172
|
+
// Hide compacting status animation on error
|
|
7173
|
+
if (this.renderer) {
|
|
7174
|
+
this.renderer.hideCompactingStatus();
|
|
7175
|
+
}
|
|
7176
7176
|
display.showError('Context compaction failed.', error);
|
|
7177
7177
|
}
|
|
7178
7178
|
finally {
|
|
7179
7179
|
if (cleanupOverlayActive) {
|
|
7180
7180
|
this.statusTracker.clearOverride(cleanupStatusId);
|
|
7181
7181
|
}
|
|
7182
|
+
// Ensure compacting status is cleared
|
|
7183
|
+
if (this.renderer) {
|
|
7184
|
+
this.renderer.hideCompactingStatus();
|
|
7185
|
+
}
|
|
7182
7186
|
this.cleanupInProgress = false;
|
|
7183
7187
|
this.contextCompactionInFlight = false;
|
|
7184
7188
|
}
|
|
@@ -7480,28 +7484,6 @@ Return ONLY JSON array:
|
|
|
7480
7484
|
const message = error instanceof Error ? error.message : String(error);
|
|
7481
7485
|
display.showError(message);
|
|
7482
7486
|
}
|
|
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
|
-
}
|
|
7505
7487
|
handleProviderError(error, retryAction) {
|
|
7506
7488
|
const promptBlock = detectPromptBlockError(error);
|
|
7507
7489
|
if (promptBlock) {
|