minovative-mind-cli 2.5.2 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +29 -26
  2. package/dist/commands/chat.js +2 -2
  3. package/dist/services/agent/inputHandler.d.ts +9 -0
  4. package/dist/services/agent/inputHandler.js +34 -0
  5. package/dist/services/agent/slashCommands.js +105 -29
  6. package/dist/services/agent/syntaxAgent.d.ts +40 -0
  7. package/dist/services/agent/syntaxAgent.js +237 -23
  8. package/dist/services/agent/toolLoop.js +10 -1
  9. package/dist/services/agent/types.d.ts +1 -0
  10. package/dist/services/agent-tools.d.ts +156 -1
  11. package/dist/services/agent-tools.js +259 -67
  12. package/dist/services/agent.d.ts +74 -0
  13. package/dist/services/agent.js +193 -31
  14. package/dist/services/ai.d.ts +5 -0
  15. package/dist/services/ai.js +80 -87
  16. package/dist/services/chatHistoryService.d.ts +11 -0
  17. package/dist/services/chatHistoryService.js +20 -1
  18. package/dist/services/contextAgent.d.ts +1 -1
  19. package/dist/services/contextAgent.js +9 -29
  20. package/dist/services/orchestration/investigationAgent.d.ts +2 -1
  21. package/dist/services/orchestration/investigationAgent.js +7 -2
  22. package/dist/services/orchestration/investigationOrchestrator.d.ts +1 -1
  23. package/dist/services/orchestration/investigationOrchestrator.js +13 -2
  24. package/dist/services/orchestration/orchestrator.js +21 -4
  25. package/dist/services/orchestration/subAgent.d.ts +2 -1
  26. package/dist/services/orchestration/subAgent.js +12 -6
  27. package/dist/utils/analysisRunner.d.ts +27 -4
  28. package/dist/utils/analysisRunner.js +100 -20
  29. package/dist/utils/config.d.ts +2 -0
  30. package/dist/utils/config.js +2 -0
  31. package/dist/utils/fuzzyMatch.d.ts +32 -0
  32. package/dist/utils/fuzzyMatch.js +215 -27
  33. package/dist/utils/localSyntaxValidator.d.ts +2 -2
  34. package/dist/utils/localSyntaxValidator.js +280 -81
  35. package/dist/utils/performanceAuditor.d.ts +2 -7
  36. package/dist/utils/performanceAuditor.js +541 -89
  37. package/dist/utils/projectStorage.js +9 -0
  38. package/dist/utils/systemPrompts.d.ts +3 -2
  39. package/dist/utils/systemPrompts.js +29 -5
  40. package/oclif.manifest.json +2 -2
  41. package/package.json +1 -1
@@ -48,13 +48,61 @@ export { AsyncInputHandler } from './agent/inputHandler.js';
48
48
  * @param workspaceRoot - The relative or absolute path representing the active workspace environment.
49
49
  * @param version - The Semantic Version string of the active tool distribution.
50
50
  */
51
+ /**
52
+ * Starts and orchestrates the primary interactive command-line interface (REPL) loop.
53
+ *
54
+ * This is the central control loop of the CLI application. It runs indefinitely until
55
+ * the user explicitly issues an exit directive (e.g., typing "exit", "quit", `/` command menu selections,
56
+ * or using keyboard breaks like Ctrl+C).
57
+ *
58
+ * ### Architectural Pipeline:
59
+ * 1. **User Input Collection**:
60
+ * - Captures multiline text strings by checking for trailing backslash characters (`\`).
61
+ * - Prevents visual terminal glitches during multi-line typing on varying themes.
62
+ * - Automatically handles command menu redirection when users enter a forward slash (`/`).
63
+ * - Intercepts and filters OS clipboard paste buffers to allow inserting massive scripts cleanly.
64
+ *
65
+ * 2. **Slash Commands Processing**:
66
+ * - Delegated to `handleSlashCommand` within `src/services/agent/slashCommands.ts`.
67
+ *
68
+ * 3. **Workspace Context Gathering & Intention Routing**:
69
+ * - Leverages the Context Agent (`gatherContext`) to perform exploratory scans of the active repository.
70
+ * - Identifies user intention to hot-swap system prompt strategies (`CHAT` for conversational assistance vs. `EXECUTE` for multi-turn modifications).
71
+ * - Compresses gathered repo structures and file contents using high-speed Flash-Lite engines to respect context boundaries and budget costs.
72
+ *
73
+ * 4. **Model Execution & Self-Correction Feedback Loop**:
74
+ * - Passes the target request to Gemini alongside compressed workspace injections.
75
+ * - Recursively processes tool invocations via `processResponse`.
76
+ * - Runs static validation checks (`verifyChangedFiles`) against mutated files to check for compiler/linter bugs.
77
+ * - If files fail validation, enters a self-healing loop by submitting raw diagnostic errors directly back to the AI for remediation.
78
+ *
79
+ * @param workspaceRoot - The relative or absolute path representing the active workspace environment.
80
+ * @param version - The Semantic Version string of the active tool distribution.
81
+ * @returns A promise that resolves when the agent loop terminates.
82
+ */
51
83
  export declare function startAgentLoop(workspaceRoot: string, version: string): Promise<void>;
84
+ /**
85
+ * Executes a single conversational or execution turn with the AI agent.
86
+ *
87
+ * Handles context gathering, model routing (Auto vs Flash vs Flash-Lite), orchestrator delegation
88
+ * (if sub-agents are active), tool execution loops, verification, and session auto-saving.
89
+ *
90
+ * @param workspaceRoot - The absolute or relative path to the root of the active workspace.
91
+ * @param userInput - The prompt or instruction entered by the user.
92
+ * @param chat - The active ProxyChatSession instance maintaining message history.
93
+ * @param inputHandler - The AsyncInputHandler instance for managing keyboard interrupts and queueing.
94
+ * @param chatSessionState - Object tracking session statistics, token counts, credit usage, and model metrics.
95
+ * @param isPlanMode - Flag indicating whether plan mode (read-only architecture planning) is active.
96
+ * @param cachedContextResult - Optional pre-gathered context result to bypass duplicate investigation turns.
97
+ * @returns An object containing optional plan mode actions or context results, or void.
98
+ */
52
99
  export declare function executeSingleTurn(workspaceRoot: string, userInput: string, chat: any, inputHandler: AsyncInputHandler, chatSessionState: {
53
100
  id: string;
54
101
  title: string;
55
102
  totalTokens: number;
56
103
  totalInputTokens: number;
57
104
  totalOutputTokens: number;
105
+ totalCreditsUsed: number;
58
106
  latestUsageMetadata?: any;
59
107
  previousUsageMetadata?: any;
60
108
  modelUsageCounts?: Record<string, number>;
@@ -62,3 +110,29 @@ export declare function executeSingleTurn(workspaceRoot: string, userInput: stri
62
110
  planModeReturn?: string;
63
111
  contextResult?: any;
64
112
  } | void>;
113
+ /**
114
+ * Raw chat history entry count threshold to trigger chat history summarization.
115
+ * 12 entries = 6 conversational user/model turns.
116
+ */
117
+ export declare const HISTORY_SUMMARIZATION_THRESHOLD = 12;
118
+ /**
119
+ * Checks and summarizes older chat history entries if the active session's history
120
+ * exceeds HISTORY_SUMMARIZATION_THRESHOLD entries.
121
+ *
122
+ * Preserves the most recent 4 entries (2 turns) intact for conversational continuity,
123
+ * replacing all preceding turns with a single summarized pair in the chat history.
124
+ *
125
+ * @param chat - The active ProxyChatSession instance.
126
+ * @returns True if history was summarized and updated; false otherwise.
127
+ */
128
+ /**
129
+ * Summarizes older chat history entries if the active session's history
130
+ * exceeds {@link HISTORY_SUMMARIZATION_THRESHOLD} entries.
131
+ *
132
+ * Preserves the most recent 4 entries (2 turns) intact for conversational continuity,
133
+ * replacing all preceding turns with a single summarized pair in the chat history.
134
+ *
135
+ * @param chat - The active ProxyChatSession instance.
136
+ * @returns A promise resolving to true if history was summarized and updated; false otherwise.
137
+ */
138
+ export declare function summarizeHistoryIfNeeded(chat: any): Promise<boolean>;
@@ -26,8 +26,8 @@ import { markedTerminal } from 'marked-terminal';
26
26
  const execAsync = promisify(exec);
27
27
  import { debugLog, isDebugOn } from '../utils/logger.js';
28
28
  import { ensureProjectStorage, ensureIgnored, readCache, writeCache, invalidateCacheForDependents, } from '../utils/projectStorage.js';
29
- import { GEMINI_MODELS } from '../utils/config.js';
30
- import { createSharedChatSession, getGeneralChatConfig, getPlanExecutionConfig, compressTextUsingFlashLite, generateChatTitle, getGlobalActiveModel, setGlobalActiveModel, } from './ai.js';
29
+ import { GEMINI_MODELS, isByokEnabled } from '../utils/config.js';
30
+ import { createSharedChatSession, getGeneralChatConfig, getPlanExecutionConfig, compressTextUsingFlashLite, generateChatTitle, getGlobalActiveModel, setGlobalActiveModel, summarizeChatHistory, } from './ai.js';
31
31
  import { getAndResetTurnUsage } from './proxyClient.js';
32
32
  import { changeLogger } from './changeLogger.js';
33
33
  import { chatHistoryService } from './chatHistoryService.js';
@@ -77,6 +77,38 @@ export { AsyncInputHandler } from './agent/inputHandler.js';
77
77
  * @param workspaceRoot - The relative or absolute path representing the active workspace environment.
78
78
  * @param version - The Semantic Version string of the active tool distribution.
79
79
  */
80
+ /**
81
+ * Starts and orchestrates the primary interactive command-line interface (REPL) loop.
82
+ *
83
+ * This is the central control loop of the CLI application. It runs indefinitely until
84
+ * the user explicitly issues an exit directive (e.g., typing "exit", "quit", `/` command menu selections,
85
+ * or using keyboard breaks like Ctrl+C).
86
+ *
87
+ * ### Architectural Pipeline:
88
+ * 1. **User Input Collection**:
89
+ * - Captures multiline text strings by checking for trailing backslash characters (`\`).
90
+ * - Prevents visual terminal glitches during multi-line typing on varying themes.
91
+ * - Automatically handles command menu redirection when users enter a forward slash (`/`).
92
+ * - Intercepts and filters OS clipboard paste buffers to allow inserting massive scripts cleanly.
93
+ *
94
+ * 2. **Slash Commands Processing**:
95
+ * - Delegated to `handleSlashCommand` within `src/services/agent/slashCommands.ts`.
96
+ *
97
+ * 3. **Workspace Context Gathering & Intention Routing**:
98
+ * - Leverages the Context Agent (`gatherContext`) to perform exploratory scans of the active repository.
99
+ * - Identifies user intention to hot-swap system prompt strategies (`CHAT` for conversational assistance vs. `EXECUTE` for multi-turn modifications).
100
+ * - Compresses gathered repo structures and file contents using high-speed Flash-Lite engines to respect context boundaries and budget costs.
101
+ *
102
+ * 4. **Model Execution & Self-Correction Feedback Loop**:
103
+ * - Passes the target request to Gemini alongside compressed workspace injections.
104
+ * - Recursively processes tool invocations via `processResponse`.
105
+ * - Runs static validation checks (`verifyChangedFiles`) against mutated files to check for compiler/linter bugs.
106
+ * - If files fail validation, enters a self-healing loop by submitting raw diagnostic errors directly back to the AI for remediation.
107
+ *
108
+ * @param workspaceRoot - The relative or absolute path representing the active workspace environment.
109
+ * @param version - The Semantic Version string of the active tool distribution.
110
+ * @returns A promise that resolves when the agent loop terminates.
111
+ */
80
112
  export async function startAgentLoop(workspaceRoot, version) {
81
113
  // Initialize project-level storage and ensure it is safely ignored from version control
82
114
  ensureProjectStorage(workspaceRoot);
@@ -92,6 +124,7 @@ export async function startAgentLoop(workspaceRoot, version) {
92
124
  totalTokens: 0,
93
125
  totalInputTokens: 0,
94
126
  totalOutputTokens: 0,
127
+ totalCreditsUsed: 0,
95
128
  modelUsageCounts: {},
96
129
  };
97
130
  chat.setSessionInfo(chatSessionState.id, workspaceRoot);
@@ -114,6 +147,8 @@ export async function startAgentLoop(workspaceRoot, version) {
114
147
  }
115
148
  return originalEmit(event, ...args);
116
149
  };
150
+ // Add space at the bottom to prevent terminal UI flickering when the cursor is at the absolute bottom
151
+ process.stdout.write('\n\n\n\x1b[3A');
117
152
  console.log(pc.dim('\nType your coding request below. Type "exit" or "quit" to leave.\n'));
118
153
  while (true) {
119
154
  if (chat.getRawHistory().length === 0 && chatSessionState.title !== '') {
@@ -122,6 +157,7 @@ export async function startAgentLoop(workspaceRoot, version) {
122
157
  chatSessionState.totalTokens = 0;
123
158
  chatSessionState.totalInputTokens = 0;
124
159
  chatSessionState.totalOutputTokens = 0;
160
+ chatSessionState.totalCreditsUsed = 0;
125
161
  chatSessionState.modelUsageCounts = {};
126
162
  chatSessionState.latestUsageMetadata = undefined;
127
163
  chatSessionState.previousUsageMetadata = undefined;
@@ -171,7 +207,7 @@ export async function startAgentLoop(workspaceRoot, version) {
171
207
  label: '/workspaces',
172
208
  hint: 'Manage external workspaces for cross-project development',
173
209
  },
174
- { value: '/config-key', label: '/config-key', hint: 'BYOK (Bring Your Own Key) Configuration' },
210
+ { value: '/config-key', label: '/config-key', hint: 'BYOK Use your own Google AI Studio API key' },
175
211
  { value: '/stats', label: '/stats', hint: 'View current session statistics and configuration' },
176
212
  { value: '/commit', label: '/commit', hint: 'Auto-commit changes with AI message' },
177
213
  { value: '/revert', label: '/revert', hint: 'Undo last change' },
@@ -253,6 +289,21 @@ export async function startAgentLoop(workspaceRoot, version) {
253
289
  }
254
290
  }
255
291
  }
292
+ /**
293
+ * Executes a single conversational or execution turn with the AI agent.
294
+ *
295
+ * Handles context gathering, model routing (Auto vs Flash vs Flash-Lite), orchestrator delegation
296
+ * (if sub-agents are active), tool execution loops, verification, and session auto-saving.
297
+ *
298
+ * @param workspaceRoot - The absolute or relative path to the root of the active workspace.
299
+ * @param userInput - The prompt or instruction entered by the user.
300
+ * @param chat - The active ProxyChatSession instance maintaining message history.
301
+ * @param inputHandler - The AsyncInputHandler instance for managing keyboard interrupts and queueing.
302
+ * @param chatSessionState - Object tracking session statistics, token counts, credit usage, and model metrics.
303
+ * @param isPlanMode - Flag indicating whether plan mode (read-only architecture planning) is active.
304
+ * @param cachedContextResult - Optional pre-gathered context result to bypass duplicate investigation turns.
305
+ * @returns An object containing optional plan mode actions or context results, or void.
306
+ */
256
307
  export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHandler, chatSessionState, isPlanMode, cachedContextResult) {
257
308
  return runWithAgentId('main', async () => {
258
309
  const { resetTurnAccumulator } = await import('./metrics.js');
@@ -265,6 +316,20 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
265
316
  inputHandler.start(spinner);
266
317
  changeLogger.startChangeSet(userInput);
267
318
  let finalInput = userInput;
319
+ // Stage 0: Summarize chat history if length threshold is met
320
+ if (!cachedContextResult) {
321
+ const isDebug = isDebugOn();
322
+ if (!inputHandler.isCurrentlyPrompting() && !isDebug) {
323
+ spinner.start('Checking conversation history...');
324
+ }
325
+ const historySummarized = await summarizeHistoryIfNeeded(chat);
326
+ if (historySummarized) {
327
+ debugLog('Chat history was summarized and compressed prior to context investigation.');
328
+ }
329
+ if (!inputHandler.isCurrentlyPrompting() && !isDebug) {
330
+ spinner.stop();
331
+ }
332
+ }
268
333
  // Stage 1: Gather Workspace Context and route intentions
269
334
  spinner.start('🔍 Investigating workspace...');
270
335
  let gatherRes = { targetAgent: 'EXECUTE', chainedMessages: [], contextResult: cachedContextResult || null };
@@ -273,13 +338,26 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
273
338
  if (collector)
274
339
  collector.startTimer('contextGather');
275
340
  const chatHistory = chat.getRecentHistory(3);
341
+ const toolLogs = [];
276
342
  gatherRes = await gatherContext(workspaceRoot, userInput, chatHistory, inputHandler, ac.signal, (msg) => {
277
343
  if (!inputHandler.isCurrentlyPrompting()) {
278
344
  spinner.message(`🔍 Investigating workspace... ${pc.dim(msg)}`);
279
345
  }
346
+ }, (toolMsg, label) => {
347
+ const formattedLog = `${pc.dim(`[${label}]`)} ${toolMsg}`;
348
+ toolLogs.push(formattedLog);
349
+ if (!inputHandler.isCurrentlyPrompting()) {
350
+ spinner.message(`🔍 Investigating workspace... ${formattedLog}`);
351
+ }
280
352
  });
281
353
  if (collector)
282
354
  collector.stopTimer('contextGather');
355
+ // Print all collected logs at once to prevent flickering, while spinner is stopped
356
+ if (toolLogs.length > 0) {
357
+ spinner.stop();
358
+ toolLogs.forEach(log => p.log.step(log));
359
+ spinner.start(`🔍 Context gathered successfully.`);
360
+ }
283
361
  }
284
362
  let latestUsage = undefined;
285
363
  // Collect any inputs that were queued while the Context Agent was investigating
@@ -379,6 +457,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
379
457
  catch { }
380
458
  if (!isPlanMode && effectiveTargetAgent === 'EXECUTE' && isSubAgentsEnabled()) {
381
459
  spinner.stop(); // Clear the spinner before delegating
460
+ inputHandler.clearSpinner(); // Prevent prompt from reviving the old spinner
382
461
  process.stdout.write('\x1b[2K\r');
383
462
  const orchestrator = new Orchestrator(workspaceRoot, chatSessionState.id, inputHandler);
384
463
  const handledByOrchestrator = await orchestrator.runOrchestration(finalInput, dynamicSystemInstruction, ac.signal);
@@ -413,6 +492,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
413
492
  chatSessionState.totalTokens += usage.totalTokenCount;
414
493
  chatSessionState.totalInputTokens += (usage.promptTokens || 0) + (usage.cachedTokens || 0);
415
494
  chatSessionState.totalOutputTokens += usage.candidatesTokens || 0;
495
+ chatSessionState.totalCreditsUsed += usage.creditsUsed || 0;
416
496
  if (usage.cachedTokens && usage.cachedTokens > 0) {
417
497
  const totalInputTokens = (usage.promptTokens || 0) + usage.cachedTokens;
418
498
  const percentSaved = totalInputTokens > 0 ? Math.round((usage.cachedTokens / totalInputTokens) * 100) : 0;
@@ -459,6 +539,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
459
539
  totalTokens: chatSessionState.totalTokens,
460
540
  totalInputTokens: chatSessionState.totalInputTokens,
461
541
  totalOutputTokens: chatSessionState.totalOutputTokens,
542
+ totalCreditsUsed: chatSessionState.totalCreditsUsed,
462
543
  gitBranch: gitBranch || undefined,
463
544
  autoApprove: getApprovalMode() === 'skip-all',
464
545
  subAgents: isSubAgentsEnabled(),
@@ -559,6 +640,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
559
640
  chatSessionState.totalTokens += usage.totalTokenCount || 0;
560
641
  chatSessionState.totalInputTokens += (usage.promptTokens || 0) + (usage.cachedTokens || 0);
561
642
  chatSessionState.totalOutputTokens += usage.candidatesTokens || 0;
643
+ chatSessionState.totalCreditsUsed += usage.creditsUsed || 0;
562
644
  }
563
645
  // Auto-save chat history
564
646
  const history = chat.getRawHistory();
@@ -579,6 +661,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
579
661
  totalTokens: chatSessionState.totalTokens,
580
662
  totalInputTokens: chatSessionState.totalInputTokens,
581
663
  totalOutputTokens: chatSessionState.totalOutputTokens,
664
+ totalCreditsUsed: chatSessionState.totalCreditsUsed,
582
665
  gitBranch: gitBranch || undefined,
583
666
  autoApprove: getApprovalMode() === 'skip-all',
584
667
  subAgents: isSubAgentsEnabled(),
@@ -603,6 +686,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
603
686
  totalTokens: chatSessionState.totalTokens,
604
687
  totalInputTokens: chatSessionState.totalInputTokens,
605
688
  totalOutputTokens: chatSessionState.totalOutputTokens,
689
+ totalCreditsUsed: chatSessionState.totalCreditsUsed,
606
690
  gitBranch: gitBranch || undefined,
607
691
  autoApprove: getApprovalMode() === 'skip-all',
608
692
  subAgents: isSubAgentsEnabled(),
@@ -636,31 +720,43 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
636
720
  ],
637
721
  });
638
722
  if (planAction === 'choose_model') {
723
+ const byokEnabled = await isByokEnabled();
724
+ const options = [
725
+ // {
726
+ // value: 'claude-opus-5',
727
+ // label: 'Claude 5 Opus',
728
+ // hint: 'For hardcore & complex problems',
729
+ // },
730
+ {
731
+ value: 'gemini-3.1-pro-preview',
732
+ label: 'Gemini 3.1 Pro',
733
+ hint: 'For hardcore & complex problems',
734
+ },
735
+ // {
736
+ // value: 'claude-sonnet-5',
737
+ // label: 'Claude 5 Sonnet',
738
+ // hint: 'Best for raw speed and cost efficiency',
739
+ // },
740
+ {
741
+ value: 'gemini-3.6-flash',
742
+ label: 'Gemini 3.6 Flash',
743
+ hint: 'Balanced performance & fast',
744
+ },
745
+ {
746
+ value: 'gemini-3.5-flash-lite',
747
+ label: 'Gemini 3.5 Flash-Lite',
748
+ hint: 'Best for raw speed and cost efficiency',
749
+ },
750
+ {
751
+ value: 'auto',
752
+ label: 'Auto (Flash-Lite / Flash)',
753
+ hint: 'Dynamically routes between Gemini 3.5 Flash-Lite and Gemini 3.6 based on prompt complexity',
754
+ },
755
+ ].filter(o => !(byokEnabled && o.value.includes('claude')));
639
756
  const selectedModel = await p['select']({
640
757
  message: `Select AI Model (Current: ${pc.cyan(currentModel)})`,
641
758
  initialValue: currentModel,
642
- options: [
643
- {
644
- value: 'gemini-3.1-pro-preview',
645
- label: 'Gemini 3.1 Pro',
646
- hint: 'The Pro model for complex logic',
647
- },
648
- {
649
- value: 'gemini-3.6-flash',
650
- label: 'Gemini 3.6 Flash',
651
- hint: 'Balanced performance',
652
- },
653
- {
654
- value: 'gemini-3.5-flash-lite',
655
- label: 'Gemini 3.5 Flash-Lite',
656
- hint: 'Best for speed and cost efficiency',
657
- },
658
- {
659
- value: 'auto',
660
- label: 'Auto (Flash-Lite / Flash)',
661
- hint: 'Dynamically routes between Gemini 3.5 Flash-Lite and Gemini 3.6 based on prompt complexity',
662
- },
663
- ],
759
+ options: options,
664
760
  });
665
761
  if (!p.isCancel(selectedModel)) {
666
762
  chat.setModel(selectedModel);
@@ -704,7 +800,7 @@ async function collectUserInput(history, isPlanMode) {
704
800
  while (true) {
705
801
  const userInputRaw = await historyText({
706
802
  message: isMultiLine ? ' ' : isPlanMode ? pc.cyan('(Plan Mode) ❯') : pc.magenta('❯'),
707
- placeholder: isMultiLine ? '' : 'Use "\\" for new lines',
803
+ placeholder: isMultiLine ? '' : 'Use ' + String.fromCharCode(92) + ' for new lines',
708
804
  history,
709
805
  });
710
806
  if (p.isCancel(userInputRaw)) {
@@ -720,7 +816,7 @@ async function collectUserInput(history, isPlanMode) {
720
816
  }
721
817
  let line = (userInputRaw || '');
722
818
  // Require a space before the backslash to prevent Windows directory paths (e.g., C:\foo\) from triggering multiline
723
- if (line.trimEnd().endsWith(' \\')) {
819
+ if (line.trimEnd().endsWith(' ' + String.fromCharCode(92))) {
724
820
  const cleanLine = line.trimEnd().slice(0, -2);
725
821
  lines.push(cleanLine);
726
822
  isMultiLine = true;
@@ -782,6 +878,64 @@ async function compressContextFiles(workspaceRoot, contextResult) {
782
878
  }
783
879
  return compressedFiles;
784
880
  }
881
+ /**
882
+ * Raw chat history entry count threshold to trigger chat history summarization.
883
+ * 12 entries = 6 conversational user/model turns.
884
+ */
885
+ export const HISTORY_SUMMARIZATION_THRESHOLD = 12;
886
+ /**
887
+ * Checks and summarizes older chat history entries if the active session's history
888
+ * exceeds HISTORY_SUMMARIZATION_THRESHOLD entries.
889
+ *
890
+ * Preserves the most recent 4 entries (2 turns) intact for conversational continuity,
891
+ * replacing all preceding turns with a single summarized pair in the chat history.
892
+ *
893
+ * @param chat - The active ProxyChatSession instance.
894
+ * @returns True if history was summarized and updated; false otherwise.
895
+ */
896
+ /**
897
+ * Summarizes older chat history entries if the active session's history
898
+ * exceeds {@link HISTORY_SUMMARIZATION_THRESHOLD} entries.
899
+ *
900
+ * Preserves the most recent 4 entries (2 turns) intact for conversational continuity,
901
+ * replacing all preceding turns with a single summarized pair in the chat history.
902
+ *
903
+ * @param chat - The active ProxyChatSession instance.
904
+ * @returns A promise resolving to true if history was summarized and updated; false otherwise.
905
+ */
906
+ export async function summarizeHistoryIfNeeded(chat) {
907
+ const history = chat.getRawHistory();
908
+ if (!history || history.length < HISTORY_SUMMARIZATION_THRESHOLD) {
909
+ return false;
910
+ }
911
+ try {
912
+ // Keep the most recent 4 entries (2 full user/model turns) intact
913
+ const recentCount = 4;
914
+ const olderHistory = history.slice(0, history.length - recentCount);
915
+ const recentHistory = history.slice(history.length - recentCount);
916
+ debugLog(`Summarizing ${olderHistory.length} older chat history entries...`);
917
+ const summaryText = await summarizeChatHistory(olderHistory);
918
+ if (summaryText && summaryText.trim().length > 0) {
919
+ const summaryContent = [
920
+ {
921
+ role: 'user',
922
+ parts: [{ text: `[PREVIOUS CONVERSATION SUMMARY]:\n${summaryText.trim()}` }],
923
+ },
924
+ {
925
+ role: 'model',
926
+ parts: [{ text: 'Understood. I have reviewed and absorbed the summary of our preceding conversation.' }],
927
+ },
928
+ ];
929
+ chat.loadRawHistory([...summaryContent, ...recentHistory]);
930
+ debugLog(`Chat history compressed successfully from ${history.length} to ${summaryContent.length + recentHistory.length} entries.`);
931
+ return true;
932
+ }
933
+ }
934
+ catch (error) {
935
+ debugLog(`Failed to summarize chat history: ${error?.message || error}`);
936
+ }
937
+ return false;
938
+ }
785
939
  async function executeSelfCorrectionLoop(chat, initialResult, workspaceRoot, inputHandler, effectiveTargetAgent, signal, spinner, originalUserInput, isPlanMode) {
786
940
  let correctionAttempts = 0;
787
941
  const MAX_CORRECTIONS = 5;
@@ -795,16 +949,24 @@ async function executeSelfCorrectionLoop(chat, initialResult, workspaceRoot, inp
795
949
  if (finalText === '[Generation stopped by user]')
796
950
  break;
797
951
  const historyLengthBefore = chat.getRawHistory().length;
798
- finalText = await processResponse(chat, result, workspaceRoot, inputHandler, agentState, signal);
952
+ const currentText = await processResponse(chat, result, workspaceRoot, inputHandler, agentState, signal);
799
953
  const historyLengthAfter = chat.getRawHistory().length;
800
954
  const usedTools = historyLengthAfter > historyLengthBefore;
801
- debugLog(`processResponse returned finalText (length ${finalText.length}): "${finalText.substring(0, 10)}..."`);
802
- if (finalText === '[Generation stopped by user]') {
955
+ debugLog(`processResponse returned text (length ${currentText.length}): "${currentText.substring(0, 10)}..."`);
956
+ if (currentText === '[Generation stopped by user]') {
957
+ finalText = currentText;
803
958
  break;
804
959
  }
805
- if (finalText.startsWith('[The AI repeatedly returned empty responses')) {
960
+ if (currentText.startsWith('[The AI repeatedly returned empty responses')) {
961
+ finalText = currentText;
806
962
  break;
807
963
  }
964
+ if (correctionAttempts === 0) {
965
+ finalText = currentText;
966
+ }
967
+ else if (currentText && !currentText.includes('[INTENT_VERIFIED]')) {
968
+ finalText += '\n\n### Subsequent Fixes:\n' + currentText;
969
+ }
808
970
  const currentChanges = changeLogger.getCurrentChangeSet()?.changes || [];
809
971
  // If the correction cycle is active but no new changes were registered, the model gave up
810
972
  if (isFixingCodeError && currentChanges.length <= previousChangeCount && !usedTools) {
@@ -88,6 +88,11 @@ export declare function createIntentRouterSession(): any;
88
88
  export declare function createExecutionComplexitySession(): any;
89
89
  export declare function createInvestigationComplexitySession(): any;
90
90
  export declare function createWebSearchAgentSession(): any;
91
+ export declare function createHistorySummarizerSession(): any;
92
+ /**
93
+ * Summarizes an array of Content history entries using Gemini Flash Lite.
94
+ */
95
+ export declare function summarizeChatHistory(history: Content[]): Promise<string>;
91
96
  /**
92
97
  * Generates a concise title for a chat session based on the user's first message.
93
98
  */