minovative-mind-cli 2.14.1 → 2.14.3

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 (47) hide show
  1. package/README.md +40 -59
  2. package/dist/services/agent/slashCommands.js +144 -13
  3. package/dist/services/agent/toolLoop.js +11 -2
  4. package/dist/services/agent/types.d.ts +9 -2
  5. package/dist/services/agent-tools.d.ts +20 -1
  6. package/dist/services/agent-tools.js +272 -47
  7. package/dist/services/agent.js +43 -18
  8. package/dist/services/ai.d.ts +9 -0
  9. package/dist/services/ai.js +71 -21
  10. package/dist/services/chatHistoryService.d.ts +5 -0
  11. package/dist/services/contextAgent.d.ts +14 -6
  12. package/dist/services/contextAgent.js +36 -10
  13. package/dist/services/orchestration/investigationAgent.js +31 -7
  14. package/dist/services/orchestration/investigationCache.js +19 -9
  15. package/dist/services/orchestration/readCache.d.ts +1 -0
  16. package/dist/services/orchestration/readCache.js +5 -2
  17. package/dist/services/orchestration/scopedTools.js +37 -10
  18. package/dist/services/orchestration/subAgent.js +16 -2
  19. package/dist/services/proxyClient.d.ts +6 -0
  20. package/dist/services/proxyClient.js +24 -10
  21. package/dist/services/sessionSettings.d.ts +66 -0
  22. package/dist/services/sessionSettings.js +126 -0
  23. package/dist/services/userProfileService.d.ts +14 -0
  24. package/dist/services/userProfileService.js +105 -3
  25. package/dist/services/verificationService.js +24 -2
  26. package/dist/utils/analysisRunner.d.ts +120 -8
  27. package/dist/utils/analysisRunner.js +946 -125
  28. package/dist/utils/antiCheatingGuard.d.ts +21 -0
  29. package/dist/utils/antiCheatingGuard.js +554 -0
  30. package/dist/utils/contextPrompts.d.ts +39 -0
  31. package/dist/utils/contextPrompts.js +81 -9
  32. package/dist/utils/contextRanker.d.ts +216 -0
  33. package/dist/utils/contextRanker.js +603 -0
  34. package/dist/utils/dependencyTracer/modules/graph.d.ts +4 -1
  35. package/dist/utils/dependencyTracer/modules/graph.js +11 -0
  36. package/dist/utils/dependencyTracer/modules/types.d.ts +10 -0
  37. package/dist/utils/dependencyTracer.d.ts +40 -2
  38. package/dist/utils/dependencyTracer.js +95 -3
  39. package/dist/utils/fileReadCache.d.ts +58 -0
  40. package/dist/utils/fileReadCache.js +162 -0
  41. package/dist/utils/projectStorage.js +2 -1
  42. package/dist/utils/symbolExtractor.d.ts +12 -0
  43. package/dist/utils/symbolExtractor.js +111 -15
  44. package/dist/utils/systemPrompts.d.ts +4 -3
  45. package/dist/utils/systemPrompts.js +66 -8
  46. package/oclif.manifest.json +1 -1
  47. package/package.json +1 -1
@@ -26,13 +26,13 @@ const execAsync = promisify(exec);
26
26
  import { debugLog, isDebugOn } from '../utils/logger.js';
27
27
  import { ensureProjectStorage, ensureIgnored, readCache, writeCache, invalidateCacheForDependents, } from '../utils/projectStorage.js';
28
28
  import { GEMINI_MODELS, TPM_COOLING_DELAYS } from '../utils/config.js';
29
- import { createSharedChatSession, getGeneralChatConfig, getPlanExecutionConfig, compressTextUsingFlashLite, generateChatTitle, getGlobalActiveModel, getModelThinkingLevel, summarizeChatHistory, } from './ai.js';
29
+ import { createSharedChatSession, getGeneralChatConfig, getPlanExecutionConfig, getResearchConfig, compressTextUsingFlashLite, generateChatTitle, getGlobalActiveModel, getModelThinkingLevel, summarizeChatHistory, } from './ai.js';
30
30
  import { getAndResetTurnUsage } from './proxyClient.js';
31
31
  import { changeLogger } from './changeLogger.js';
32
32
  import { chatHistoryService } from './chatHistoryService.js';
33
33
  import { gatherContext, routeIntent, evaluateExecutionComplexity } from './contextAgent.js';
34
34
  import { verifyChangedFiles } from './verificationService.js';
35
- import { buildContextInjection } from '../utils/contextPrompts.js';
35
+ import { buildTieredContextInjection, CONTEXT_BUDGET_CONFIG } from '../utils/contextPrompts.js';
36
36
  import { registerContextFiles } from '../utils/fileReadGuard.js';
37
37
  import { historyText } from '../utils/historyPrompt.js';
38
38
  import { loadUserProfile, formatUserProfileForContext, extractAndSaveUserInsights } from './userProfileService.js';
@@ -43,6 +43,8 @@ import { handleSlashCommand } from './agent/slashCommands.js';
43
43
  import { getMetricCollector } from './metrics.js';
44
44
  import { Orchestrator } from './orchestration/orchestrator.js';
45
45
  import { isSubAgentsEnabled, getApprovalMode } from './agent-tools.js';
46
+ import { resetSessionSettingsToDefault } from './sessionSettings.js';
47
+ import { clearFileReadCache } from '../utils/fileReadCache.js';
46
48
  import { runWithAgentId } from '../utils/asyncContext.js';
47
49
  // Export submodules for potential external uses if required
48
50
  export { AsyncInputHandler } from './agent/inputHandler.js';
@@ -117,6 +119,8 @@ export async function startAgentLoop(workspaceRoot, version) {
117
119
  changeLogger.init(workspaceRoot);
118
120
  chatHistoryService.init(workspaceRoot);
119
121
  const chat = createSharedChatSession();
122
+ resetSessionSettingsToDefault(chat);
123
+ clearFileReadCache();
120
124
  const inputHandler = new AsyncInputHandler();
121
125
  const chatSessionState = {
122
126
  id: crypto.randomUUID(),
@@ -160,6 +164,7 @@ export async function startAgentLoop(workspaceRoot, version) {
160
164
  chatSessionState.modelUsageCounts = {};
161
165
  chatSessionState.latestUsageMetadata = undefined;
162
166
  chatSessionState.previousUsageMetadata = undefined;
167
+ resetSessionSettingsToDefault(chat);
163
168
  chat.setSessionInfo(chatSessionState.id, workspaceRoot);
164
169
  }
165
170
  inputHandler.stop();
@@ -359,21 +364,22 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
359
364
  if (gatherRes.chainedMessages.length > 0) {
360
365
  const chainedContent = gatherRes.chainedMessages.join('\n');
361
366
  const newIntent = await routeIntent(chainedContent, '', ac.signal);
362
- if (gatherRes.contextResult !== null) {
363
- // If the model previously needed search context, determine if followups change the mode
364
- effectiveTargetAgent = newIntent.targetAgent;
367
+ if (gatherRes.targetAgent === 'EXECUTE') {
368
+ // EXECUTE permission sets are immutable during follow-ups to prevent downgrades
369
+ effectiveTargetAgent = 'EXECUTE';
370
+ }
371
+ else if (gatherRes.targetAgent === 'RESEARCH') {
372
+ // RESEARCH sessions stay in RESEARCH mode during chained requests
373
+ // Chained requests are incorporated into the research context for comprehensive investigation
374
+ effectiveTargetAgent = 'RESEARCH';
365
375
  }
366
376
  else {
367
- // If not in a context-search flow
368
- if (gatherRes.targetAgent === 'EXECUTE') {
369
- // EXECUTE permission sets are immutable during follow-ups to prevent downgrades
377
+ // If in CHAT mode, escalate if chained instructions dictate
378
+ if (newIntent.targetAgent === 'EXECUTE') {
370
379
  effectiveTargetAgent = 'EXECUTE';
371
380
  }
372
- else {
373
- // Conversational prompts can escalate to EXECUTE if chained instructions dictate
374
- if (newIntent.targetAgent === 'EXECUTE' || newIntent.needsContext) {
375
- effectiveTargetAgent = 'EXECUTE';
376
- }
381
+ else if (newIntent.targetAgent === 'RESEARCH') {
382
+ effectiveTargetAgent = 'RESEARCH';
377
383
  }
378
384
  }
379
385
  finalInput += `\n\n[USER FOLLOW-UP INSTRUCTIONS SENT DURING INVESTIGATION]:\n${chainedContent}\n\nPlease incorporate these instructions into your work. Address them appropriately, but ensure you do not lose track of the original request's primary objective.`;
@@ -383,16 +389,23 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
383
389
  process.stdout.write('\x1b[2K\r');
384
390
  return;
385
391
  }
386
- // Hot-swap the underlying LLM system instruction context depending on intent (conversational vs execution)
392
+ // Hot-swap the underlying LLM system instruction context depending on intent (conversational vs research vs execution)
387
393
  debugLog(`Intent Router output: original targetAgent = ${gatherRes.targetAgent}, effective = ${effectiveTargetAgent}`);
388
- const config = effectiveTargetAgent === 'CHAT' ? getGeneralChatConfig() : getPlanExecutionConfig();
394
+ const config = effectiveTargetAgent === 'CHAT'
395
+ ? getGeneralChatConfig()
396
+ : effectiveTargetAgent === 'RESEARCH'
397
+ ? getResearchConfig()
398
+ : getPlanExecutionConfig();
389
399
  let dynamicSystemInstruction = config.systemInstruction;
390
400
  // Inject gathered workspace directories, dependency configurations, and matching search patterns
391
401
  if (gatherRes.contextResult) {
392
402
  // Compress each relevant file individually using helper to avoid nested loop warning
393
403
  gatherRes.contextResult.relevantFiles = await compressContextFiles(workspaceRoot, gatherRes.contextResult, ac.signal);
394
- // Assemble the final context injection string
395
- const contextInjection = buildContextInjection(gatherRes.contextResult);
404
+ // Assemble the final context injection string using 3-tier IR/graph context ranking
405
+ const contextInjection = buildTieredContextInjection(gatherRes.contextResult, {
406
+ prompt: userInput,
407
+ maxChars: CONTEXT_BUDGET_CONFIG.DEFAULT_CONTEXT_MAX_CHARS,
408
+ });
396
409
  debugLog(`Final compressed context injection size: ${contextInjection.length} chars`);
397
410
  // Pre-register all injected files with the read-guard so the execution
398
411
  // agent is allowed to modify them without calling read_file first.
@@ -492,6 +505,9 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
492
505
  if (effectiveTargetAgent === 'CHAT') {
493
506
  selectedModel = GEMINI_MODELS.FLASH_LITE;
494
507
  }
508
+ else if (effectiveTargetAgent === 'RESEARCH') {
509
+ selectedModel = GEMINI_MODELS.FLASH;
510
+ }
495
511
  else {
496
512
  spinner.stop(); // MUST clear the investigation spinner first to prevent leaking the setInterval
497
513
  spinner.start(pc.blue('🧠 Evaluating execution complexity...'));
@@ -658,6 +674,8 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
658
674
  creditsRemaining: usage.remainingBalance,
659
675
  lastTurnDuration: turnDuration,
660
676
  modelName: chat.getModel(),
677
+ thinkingLevel: (typeof chat.getThinkingLevel === 'function' ? chat.getThinkingLevel() : undefined) ||
678
+ getModelThinkingLevel(chat.getModel()),
661
679
  totalTokens: chatSessionState.totalTokens,
662
680
  totalInputTokens: chatSessionState.totalInputTokens,
663
681
  totalOutputTokens: chatSessionState.totalOutputTokens,
@@ -665,6 +683,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
665
683
  gitBranch: gitBranch || undefined,
666
684
  autoApprove: getApprovalMode() === 'skip-all',
667
685
  subAgents: isSubAgentsEnabled(),
686
+ debugMode: isDebugOn(),
668
687
  latestUsageMetadata: chatSessionState.latestUsageMetadata,
669
688
  previousUsageMetadata: chatSessionState.previousUsageMetadata,
670
689
  modelUsageCounts: chatSessionState.modelUsageCounts,
@@ -822,6 +841,8 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
822
841
  creditsRemaining: latestUsage?.remainingBalance,
823
842
  lastTurnDuration: turnDuration,
824
843
  modelName: chat.getModel(),
844
+ thinkingLevel: (typeof chat.getThinkingLevel === 'function' ? chat.getThinkingLevel() : undefined) ||
845
+ getModelThinkingLevel(chat.getModel()),
825
846
  totalTokens: chatSessionState.totalTokens,
826
847
  totalInputTokens: chatSessionState.totalInputTokens,
827
848
  totalOutputTokens: chatSessionState.totalOutputTokens,
@@ -829,6 +850,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
829
850
  gitBranch: gitBranch || undefined,
830
851
  autoApprove: getApprovalMode() === 'skip-all',
831
852
  subAgents: isSubAgentsEnabled(),
853
+ debugMode: isDebugOn(),
832
854
  latestUsageMetadata: chatSessionState.latestUsageMetadata,
833
855
  previousUsageMetadata: chatSessionState.previousUsageMetadata,
834
856
  modelUsageCounts: chatSessionState.modelUsageCounts,
@@ -847,6 +869,8 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
847
869
  creditsRemaining: latestUsage?.remainingBalance,
848
870
  lastTurnDuration: turnDuration,
849
871
  modelName: chat.getModel(),
872
+ thinkingLevel: (typeof chat.getThinkingLevel === 'function' ? chat.getThinkingLevel() : undefined) ||
873
+ getModelThinkingLevel(chat.getModel()),
850
874
  totalTokens: chatSessionState.totalTokens,
851
875
  totalInputTokens: chatSessionState.totalInputTokens,
852
876
  totalOutputTokens: chatSessionState.totalOutputTokens,
@@ -854,6 +878,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
854
878
  gitBranch: gitBranch || undefined,
855
879
  autoApprove: getApprovalMode() === 'skip-all',
856
880
  subAgents: isSubAgentsEnabled(),
881
+ debugMode: isDebugOn(),
857
882
  latestUsageMetadata: chatSessionState.latestUsageMetadata,
858
883
  previousUsageMetadata: chatSessionState.previousUsageMetadata,
859
884
  modelUsageCounts: chatSessionState.modelUsageCounts,
@@ -1201,7 +1226,7 @@ If you are not done, please continue working using your other tools.`;
1201
1226
  .join('\n\n');
1202
1227
  debugLog(`Verification failed on attempt ${correctionAttempts}/${MAX_CORRECTIONS}. Issues:\n${combinedIssuesForAI}`);
1203
1228
  // Compile compilation and syntax diagnostic warnings into an auto-correction prompt
1204
- const correctionPrompt = `AUTOMATED SYSTEM CHECK: Your previous changes resulted in the following issues:\n\n${combinedIssuesForAI}\n\nPlease analyze these issues and use your file modification tools to fix them.`;
1229
+ const correctionPrompt = `AUTOMATED SYSTEM CHECK: Your previous changes resulted in the following issues:\n\n${combinedIssuesForAI}\n\nCRITICAL ANTI-CHEATING RULE: You MUST fix these issues head-on in the application source code. You are STRICTLY FORBIDDEN from modifying build configurations (e.g. next.config.*, tsconfig.json, vite.config.*, .eslintrc*, package.json scripts) to ignore, disable, or bypass build, compile, type, or lint errors (such as ignoreBuildErrors, ignoreDuringBuilds, or turning off checks). You are also forbidden from adding blanket suppressions like @ts-ignore, @ts-nocheck, or eslint-disable to silence errors. Fix the underlying root cause in the actual code.`;
1205
1230
  // Cooling-off delay before initiating the self-correction turn
1206
1231
  await new Promise((resolve) => setTimeout(resolve, TPM_COOLING_DELAYS.CORRECTION_TURN_MS));
1207
1232
  if (signal.aborted)
@@ -18,6 +18,9 @@ export declare function setModelThinkingLevel(model: string, level: ThinkingLeve
18
18
  * Resets user-configured thinking levels back to default model presets.
19
19
  */
20
20
  export declare function resetModelThinkingLevels(): void;
21
+ export declare const HISTORICAL_TOOL_OUTPUT_THRESHOLD = 1500;
22
+ export declare const COLLAPSED_TOOL_OUTPUT_MARKER = "\n... [Historical tool output collapsed to save context]";
23
+ export declare function collapseHistoricalOutput(val: string, threshold?: number): string;
21
24
  export declare class ProxyChatSession {
22
25
  private history;
23
26
  private fullHistory;
@@ -114,6 +117,12 @@ export declare function getPlanExecutionConfig(): {
114
117
  functionDeclarations: import("@google/generative-ai").FunctionDeclaration[];
115
118
  }[];
116
119
  };
120
+ export declare function getResearchConfig(): {
121
+ systemInstruction: string;
122
+ tools: {
123
+ functionDeclarations: import("@google/generative-ai").FunctionDeclaration[];
124
+ }[];
125
+ };
117
126
  /**
118
127
  * Compresses a large string of text using Gemini Flash.
119
128
  * Used for shrinking context payloads to prevent OOM/choking.
@@ -5,7 +5,7 @@ import { getMetricCollector } from './metrics.js';
5
5
  import { getAuthorizedIdToken, checkByokSubscription } from './auth.js';
6
6
  import { debugLog } from '../utils/logger.js';
7
7
  import { readCache, writeCache } from '../utils/projectStorage.js';
8
- import { GENERAL_CHAT_INSTRUCTION, PLAN_EXECUTION_INSTRUCTION, CONTEXT_SYSTEM_INSTRUCTION, INTENT_ROUTER_SYSTEM_INSTRUCTION, WEB_SEARCH_SYSTEM_INSTRUCTION, EXECUTION_COMPLEXITY_SYSTEM_INSTRUCTION, INVESTIGATION_COMPLEXITY_SYSTEM_INSTRUCTION, INVESTIGATION_SEMANTIC_SYSTEM_INSTRUCTION, HISTORY_SUMMARIZER_SYSTEM_INSTRUCTION, USER_PROFILE_EXTRACTOR_SYSTEM_INSTRUCTION, TRIVIAL_MESSAGE_CLASSIFIER_SYSTEM_INSTRUCTION, } from '../utils/systemPrompts.js';
8
+ import { GENERAL_CHAT_INSTRUCTION, PLAN_EXECUTION_INSTRUCTION, RESEARCH_AGENT_SYSTEM_INSTRUCTION, CONTEXT_SYSTEM_INSTRUCTION, INTENT_ROUTER_SYSTEM_INSTRUCTION, WEB_SEARCH_SYSTEM_INSTRUCTION, EXECUTION_COMPLEXITY_SYSTEM_INSTRUCTION, INVESTIGATION_COMPLEXITY_SYSTEM_INSTRUCTION, INVESTIGATION_SEMANTIC_SYSTEM_INSTRUCTION, HISTORY_SUMMARIZER_SYSTEM_INSTRUCTION, USER_PROFILE_EXTRACTOR_SYSTEM_INSTRUCTION, TRIVIAL_MESSAGE_CLASSIFIER_SYSTEM_INSTRUCTION, } from '../utils/systemPrompts.js';
9
9
  import { workspaceRegistry } from './workspaceRegistry.js';
10
10
  import { loadCredentials } from '../utils/credentialStore.js';
11
11
  import { ProxyClient } from './proxyClient.js';
@@ -100,9 +100,9 @@ const MAX_HISTORY_ENTRIES = 500;
100
100
  * payload stays within sane memory bounds.
101
101
  */
102
102
  const MAX_PART_TEXT_LENGTH = 60_000;
103
- const HISTORICAL_TOOL_OUTPUT_THRESHOLD = 1500;
104
- const COLLAPSED_TOOL_OUTPUT_MARKER = '\n... [Historical tool output collapsed to save context]';
105
- function collapseHistoricalOutput(val, threshold = HISTORICAL_TOOL_OUTPUT_THRESHOLD) {
103
+ export const HISTORICAL_TOOL_OUTPUT_THRESHOLD = 1500;
104
+ export const COLLAPSED_TOOL_OUTPUT_MARKER = '\n... [Historical tool output collapsed to save context]';
105
+ export function collapseHistoricalOutput(val, threshold = HISTORICAL_TOOL_OUTPUT_THRESHOLD) {
106
106
  if (typeof val !== 'string')
107
107
  return String(val ?? '');
108
108
  if (val.length <= threshold || val.includes('[Historical tool output collapsed')) {
@@ -354,20 +354,10 @@ export class ProxyChatSession {
354
354
  const funcResp = part.functionResponse;
355
355
  if (funcResp.response && typeof funcResp.response === 'object') {
356
356
  const respObj = funcResp.response;
357
- if (typeof respObj.output === 'string') {
358
- respObj.output = collapseHistoricalOutput(respObj.output, threshold);
359
- }
360
- if (typeof respObj.error === 'string') {
361
- respObj.error = collapseHistoricalOutput(respObj.error, threshold);
362
- }
363
- if (typeof respObj.result === 'string') {
364
- respObj.result = collapseHistoricalOutput(respObj.result, threshold);
365
- }
366
- if (typeof respObj.stdout === 'string') {
367
- respObj.stdout = collapseHistoricalOutput(respObj.stdout, threshold);
368
- }
369
- if (typeof respObj.stderr === 'string') {
370
- respObj.stderr = collapseHistoricalOutput(respObj.stderr, threshold);
357
+ for (const key of Object.keys(respObj)) {
358
+ if (typeof respObj[key] === 'string') {
359
+ respObj[key] = collapseHistoricalOutput(respObj[key], threshold);
360
+ }
371
361
  }
372
362
  }
373
363
  }
@@ -544,6 +534,12 @@ export function getPlanExecutionConfig() {
544
534
  tools: [{ functionDeclarations: getToolDeclarations({ isExecutionAgent: true }) }],
545
535
  };
546
536
  }
537
+ export function getResearchConfig() {
538
+ return {
539
+ systemInstruction: RESEARCH_AGENT_SYSTEM_INSTRUCTION.replace('{{MULTI_WORKSPACE_BLOCK}}', getMultiWorkspaceBlock()),
540
+ tools: [{ functionDeclarations: getToolDeclarations({ isResearchAgent: true }) }],
541
+ };
542
+ }
547
543
  /**
548
544
  * Compresses a large string of text using Gemini Flash.
549
545
  * Used for shrinking context payloads to prevent OOM/choking.
@@ -701,7 +697,17 @@ export function getContextToolDeclarations() {
701
697
  },
702
698
  {
703
699
  name: 'run_analysis_script',
704
- description: 'Write and execute a disposable analysis script to structurally map code in the workspace. Use this to get exact line ranges for functions, classes, and variables by leveraging the language\'s native AST parser (e.g., TypeScript compiler API, Python ast module, go/parser). You can also use this to probe the development environment — detecting available runtimes (e.g., node --version, python3 --version), checking if ports are in use, identifying project type (monorepo, package manager), or diagnosing system-level issues (disk space, memory) that may affect execution. For complex investigation tasks, you can write lightweight ML scripts (e.g., TF-IDF cosine similarity to rank file relevance, Z-score outlier detection for anomalous log lines, K-Means clustering, or Naive Bayes classification). Default to "node" for generic math/analysis as a safe baseline, but act like a native inhabitant of the host environment — if Python, Go, Rust, or specialized libraries are available in the project context, leverage the host\'s native runtimes and standard libraries for maximum efficiency. The script is executed from a temporary directory and automatically cleaned up after execution. Output should be structured JSON to stdout. Use the results to make precise read_file calls with exact startLine/endLine instead of guessing. CRITICAL: Do not use this tool on binary, document, or non-code files (e.g. PDF, image, audio, docx).',
700
+ description: 'Write and execute a disposable analysis script to structurally map code in the workspace. ' +
701
+ 'Sandbox Features: ' +
702
+ '(1) Preloaded Helpers: "emitResult(data)" / "__emitResult(data)" sends structured JSON payloads directly to structuredResult without manual stdout parsing; "inspectSymbols(target)" / "inspectObject(target)" inspects functions, classes, and properties. ' +
703
+ '(2) Module & Path Resolution: Automatically inherits tsconfig.json path aliases (@/*), NODE_PATH, and workspace virtualenvs (.venv, venv) for Python. ' +
704
+ '(3) Diagnostics Middleware: Provides actionable [SANDBOX DIAGNOSTIC] advisories for missing imports, ESM/CJS interop, and compiler errors. ' +
705
+ 'Use this to get exact line ranges for functions, classes, and variables by leveraging the language\'s native AST parser (e.g., TypeScript compiler API, Python ast module, go/parser). ' +
706
+ 'You can also use this to probe the development environment — detecting available runtimes (e.g., node --version, python3 --version), checking if ports are in use, identifying project type (monorepo, package manager), or diagnosing system-level issues (disk space, memory) that may affect execution. ' +
707
+ 'For complex investigation tasks, you can write lightweight ML scripts (e.g., TF-IDF cosine similarity to rank file relevance, Z-score outlier detection for anomalous log lines, K-Means clustering, or Naive Bayes classification). ' +
708
+ 'Default to "node" for generic math/analysis as a safe baseline, but act like a native inhabitant of the host environment — if Python, Go, Rust, or specialized libraries are available in the project context, leverage the host\'s native runtimes and standard libraries for maximum efficiency. ' +
709
+ 'The script is executed from a temporary directory and automatically cleaned up after execution. ' +
710
+ 'Use the results to make precise read_file calls with exact startLine/endLine instead of guessing. CRITICAL: Do not use this tool on binary, document, or non-code files (e.g. PDF, image, audio, docx).',
705
711
  parameters: {
706
712
  type: SchemaType.OBJECT,
707
713
  properties: {
@@ -711,7 +717,7 @@ export function getContextToolDeclarations() {
711
717
  },
712
718
  code: {
713
719
  type: SchemaType.STRING,
714
- description: 'The analysis script code. Should output structured JSON to stdout with structural information (name, type, startLine, endLine for each code element).',
720
+ description: 'The analysis script code. Should output structured JSON to stdout or use emitResult(data) with structural information (name, type, startLine, endLine for each code element).',
715
721
  },
716
722
  targetFile: {
717
723
  type: SchemaType.STRING,
@@ -794,7 +800,7 @@ export function createIntentRouterSession() {
794
800
  },
795
801
  agent: {
796
802
  type: SchemaType.STRING,
797
- enum: ['CHAT', 'EXECUTE'],
803
+ enum: ['CHAT', 'RESEARCH', 'EXECUTE'],
798
804
  description: 'Target agent to route the request to',
799
805
  },
800
806
  },
@@ -1109,6 +1115,50 @@ export function createUserProfileExtractorSession() {
1109
1115
  },
1110
1116
  description: 'Observed cognitive decision-making, collaboration, and problem-solving traits',
1111
1117
  },
1118
+ conversationalPersona: {
1119
+ type: SchemaType.OBJECT,
1120
+ properties: {
1121
+ relationshipModel: {
1122
+ type: SchemaType.STRING,
1123
+ description: 'e.g., "collaborative-peer", "command-operator", "rubber-duck", "socratic-explorer"',
1124
+ },
1125
+ banterAffinity: {
1126
+ type: SchemaType.STRING,
1127
+ description: 'e.g., "witty-banter", "dry-professional", "warm-encouraging"',
1128
+ },
1129
+ formalityLevel: {
1130
+ type: SchemaType.STRING,
1131
+ description: 'e.g., "casual-slang", "telegraphic-concise", "polite-cordial"',
1132
+ },
1133
+ apologyTolerance: {
1134
+ type: SchemaType.STRING,
1135
+ description: 'e.g., "zero-apologies", "tolerant-empathetic"',
1136
+ },
1137
+ stressCadence: {
1138
+ type: SchemaType.STRING,
1139
+ description: 'e.g., "urgent-surgical", "calm-exploratory"',
1140
+ },
1141
+ promptingHabit: {
1142
+ type: SchemaType.STRING,
1143
+ description: 'e.g., "code-dump-deducer", "bulleted-architect", "stream-of-consciousness", "rapid-breadcrumbs"',
1144
+ },
1145
+ conversationalQuirks: {
1146
+ type: SchemaType.ARRAY,
1147
+ items: {
1148
+ type: SchemaType.STRING,
1149
+ },
1150
+ description: 'Observed recurring catchphrases, quirks, or unique conversational habits',
1151
+ },
1152
+ removeQuirks: {
1153
+ type: SchemaType.ARRAY,
1154
+ items: {
1155
+ type: SchemaType.STRING,
1156
+ },
1157
+ description: 'Outdated or superseded conversational quirks to prune',
1158
+ },
1159
+ },
1160
+ description: 'Observed interpersonal, psychological, and conversational pairing dynamics',
1161
+ },
1112
1162
  strengths: {
1113
1163
  type: SchemaType.ARRAY,
1114
1164
  items: {
@@ -1,4 +1,5 @@
1
1
  import type { Content } from '@google/generative-ai';
2
+ import type { ThinkingLevel } from '../utils/config.js';
2
3
  /**
3
4
  * Options for configuring dynamic history pruning.
4
5
  */
@@ -32,6 +33,8 @@ export interface ChatSessionData {
32
33
  lastTurnDuration?: string;
33
34
  /** Optional name of the AI model used during this session (e.g., "gemini-1.5-pro"). */
34
35
  modelName?: string;
36
+ /** Optional thinking reasoning level used during this session (e.g., "LOW", "MEDIUM", "HIGH"). */
37
+ thinkingLevel?: ThinkingLevel;
35
38
  /** Optional total cumulative tokens consumed during the session. */
36
39
  totalTokens?: number;
37
40
  /** Optional total cumulative input tokens (prompt + cached) consumed during the session. */
@@ -46,6 +49,8 @@ export interface ChatSessionData {
46
49
  autoApprove?: boolean;
47
50
  /** Optional flag indicating whether sub-agents were enabled or active during this session. */
48
51
  subAgents?: boolean;
52
+ /** Optional flag indicating whether debug diagnostic logging was enabled in this session. */
53
+ debugMode?: boolean;
49
54
  /** Optional metadata about the token usage from the very last turn. */
50
55
  latestUsageMetadata?: any;
51
56
  /** Optional metadata about the token usage from the turn prior to the last turn. */
@@ -1,19 +1,27 @@
1
+ import type { TierPartitionResult } from '../utils/contextRanker.js';
2
+ export interface ContextFileEntry {
3
+ text: string;
4
+ inlineData?: any;
5
+ scoped?: boolean;
6
+ tier?: 1 | 2 | 3;
7
+ outline?: string;
8
+ }
1
9
  export interface ContextAgentResult {
2
10
  projectTree: string;
3
11
  projectType: string;
4
- relevantFiles: Map<string, {
5
- text: string;
6
- inlineData?: any;
7
- }>;
12
+ relevantFiles: Map<string, ContextFileEntry>;
8
13
  summary: string;
9
14
  webSearchSummary?: string;
10
15
  fromMemoryBank?: boolean;
11
16
  isParallel?: boolean;
17
+ tierPartition?: TierPartitionResult;
12
18
  }
13
19
  export declare function detectProjectType(workspaceRoot: string): Promise<string>;
20
+ import type { TargetAgent } from './agent/types.js';
21
+ export type { TargetAgent } from './agent/types.js';
14
22
  export interface IntentRoute {
15
23
  needsContext: boolean;
16
- targetAgent: 'CHAT' | 'EXECUTE';
24
+ targetAgent: TargetAgent;
17
25
  }
18
26
  export declare function routeIntent(userRequest: string, chatHistory?: string, abortSignal?: AbortSignal): Promise<IntentRoute>;
19
27
  export declare function evaluateExecutionComplexity(userRequest: string, investigationSummary: string | undefined, numRelevantFiles: number, chatHistory?: string, abortSignal?: AbortSignal): Promise<'EASY' | 'HARD'>;
@@ -22,6 +30,6 @@ export declare function gatherContext(workspaceRoot: string, userRequest: string
22
30
  waitForPrompt: () => Promise<void>;
23
31
  }, abortSignal: AbortSignal, onProgress?: (msg: string) => void, onToolCall?: (msg: string, label: string) => void): Promise<{
24
32
  contextResult: ContextAgentResult | null;
25
- targetAgent: 'CHAT' | 'EXECUTE';
33
+ targetAgent: TargetAgent;
26
34
  chainedMessages: string[];
27
35
  }>;
@@ -219,9 +219,11 @@ export async function routeIntent(userRequest, chatHistory = '', abortSignal) {
219
219
  const text = result.response.text()?.trim() || '{}';
220
220
  const parsed = JSON.parse(text);
221
221
  debugLog(`Intent Router Parsed: ${JSON.stringify(parsed)}`);
222
+ const rawAgent = parsed.agent?.toUpperCase?.() || parsed.agent;
223
+ const validAgent = rawAgent === 'CHAT' ? 'CHAT' : rawAgent === 'RESEARCH' ? 'RESEARCH' : 'EXECUTE';
222
224
  return {
223
225
  needsContext: parsed.context === 'SEARCH',
224
- targetAgent: parsed.agent === 'CHAT' ? 'CHAT' : 'EXECUTE',
226
+ targetAgent: validAgent,
225
227
  };
226
228
  }
227
229
  catch (e) {
@@ -557,8 +559,8 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
557
559
  else if (call.name === 'find_recent_changes') {
558
560
  logMsg = ` [Context Agent] Looking for recently modified files`;
559
561
  }
560
- else if (call.name === 'run_analysis_script') {
561
- logMsg = ` [Context Agent] Running analysis script (${args.language})`;
562
+ else if (call.name === 'run_analysis_script' || call.name === 'run_debug_script') {
563
+ logMsg = ` [Context Agent] Running analysis script (${args.language || 'auto'})`;
562
564
  }
563
565
  if (onProgress) {
564
566
  onProgress(logMsg.trim());
@@ -745,18 +747,42 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
745
747
  },
746
748
  };
747
749
  }
748
- else if (call.name === 'run_analysis_script') {
749
- const analysisResult = await runEphemeralScript(workspaceRoot, args.language, args.code, {
750
+ else if (call.name === 'run_analysis_script' || call.name === 'run_debug_script') {
751
+ const analysisResult = await runEphemeralScript(workspaceRoot, args.language || 'auto', args.code, {
750
752
  abortSignal,
751
753
  });
752
- const rawOutput = analysisResult.exitCode === 0
753
- ? analysisResult.stdout || '(script produced no output)'
754
- : `Script failed (exit ${analysisResult.exitCode}):\n${analysisResult.stderr}`;
755
- const output = boundToolOutput(rawOutput);
754
+ let rawOutput = '';
755
+ if (analysisResult.exitCode !== 0) {
756
+ rawOutput += `Script failed with exit code ${analysisResult.exitCode}.\n`;
757
+ }
758
+ if (analysisResult.structuredResult !== undefined) {
759
+ const formattedStructured = typeof analysisResult.structuredResult === 'string'
760
+ ? analysisResult.structuredResult
761
+ : JSON.stringify(analysisResult.structuredResult, null, 2);
762
+ rawOutput += `[STRUCTURED RESULT]\n${formattedStructured}\n`;
763
+ }
764
+ if (analysisResult.stdout) {
765
+ rawOutput += `[STDOUT]\n${analysisResult.stdout}\n`;
766
+ }
767
+ if (analysisResult.stderr) {
768
+ rawOutput += `[STDERR]\n${analysisResult.stderr}\n`;
769
+ }
770
+ if (!rawOutput.trim()) {
771
+ rawOutput = '(script produced no output)';
772
+ }
773
+ const output = boundToolOutput(rawOutput.trim());
756
774
  return {
757
775
  functionResponse: {
758
776
  name: call.name,
759
- response: { output },
777
+ response: {
778
+ output,
779
+ ...(analysisResult.structuredResult !== undefined
780
+ ? { structuredResult: analysisResult.structuredResult }
781
+ : {}),
782
+ ...(analysisResult.exitCode !== 0
783
+ ? { error: `Script failed (exit ${analysisResult.exitCode}):\n${analysisResult.stderr}` }
784
+ : {}),
785
+ },
760
786
  },
761
787
  };
762
788
  }
@@ -329,19 +329,43 @@ export class InvestigationAgentRunner {
329
329
  },
330
330
  };
331
331
  }
332
- else if (call.name === 'run_analysis_script') {
332
+ else if (call.name === 'run_analysis_script' || call.name === 'run_debug_script') {
333
333
  if (onProgress)
334
- onProgress(`${logPrefix} Running analysis script`);
335
- const analysisResult = await runEphemeralScript(this.workspaceRoot, args.language, args.code, {
334
+ onProgress(`${logPrefix} Running analysis script (${args.language || 'auto'})`);
335
+ const analysisResult = await runEphemeralScript(this.workspaceRoot, args.language || 'auto', args.code, {
336
336
  abortSignal,
337
337
  });
338
- const output = analysisResult.exitCode === 0
339
- ? analysisResult.stdout || '(script produced no output)'
340
- : `Script failed (exit ${analysisResult.exitCode}):\n${analysisResult.stderr}`;
338
+ let rawOutput = '';
339
+ if (analysisResult.exitCode !== 0) {
340
+ rawOutput += `Script failed with exit code ${analysisResult.exitCode}.\n`;
341
+ }
342
+ if (analysisResult.structuredResult !== undefined) {
343
+ const formattedStructured = typeof analysisResult.structuredResult === 'string'
344
+ ? analysisResult.structuredResult
345
+ : JSON.stringify(analysisResult.structuredResult, null, 2);
346
+ rawOutput += `[STRUCTURED RESULT]\n${formattedStructured}\n`;
347
+ }
348
+ if (analysisResult.stdout) {
349
+ rawOutput += `[STDOUT]\n${analysisResult.stdout}\n`;
350
+ }
351
+ if (analysisResult.stderr) {
352
+ rawOutput += `[STDERR]\n${analysisResult.stderr}\n`;
353
+ }
354
+ if (!rawOutput.trim()) {
355
+ rawOutput = '(script produced no output)';
356
+ }
341
357
  return {
342
358
  functionResponse: {
343
359
  name: call.name,
344
- response: { output },
360
+ response: {
361
+ output: rawOutput.trim(),
362
+ ...(analysisResult.structuredResult !== undefined
363
+ ? { structuredResult: analysisResult.structuredResult }
364
+ : {}),
365
+ ...(analysisResult.exitCode !== 0
366
+ ? { error: `Script failed (exit ${analysisResult.exitCode}):\n${analysisResult.stderr}` }
367
+ : {}),
368
+ },
345
369
  },
346
370
  };
347
371
  }
@@ -332,20 +332,21 @@ function tokenJaccardSimilarity(a, b) {
332
332
  export async function generateWorkspaceFingerprint(workspaceRoot, relevantFiles) {
333
333
  const fileStats = [];
334
334
  for (const file of relevantFiles) {
335
+ const normFile = file.replace(/\\/g, '/').toLowerCase();
335
336
  try {
336
- const fullPath = path.join(workspaceRoot, file);
337
+ const fullPath = path.isAbsolute(file) ? file : path.join(workspaceRoot, file);
337
338
  const stat = await fs.stat(fullPath);
338
- fileStats.push(`${file}:${stat.mtimeMs}:${stat.size}`);
339
+ fileStats.push(`${normFile}:${stat.mtimeMs}:${stat.size}`);
339
340
  }
340
341
  catch (e) {
341
342
  if (e.code === 'ENOENT') {
342
- fileStats.push(`${file}:deleted`);
343
+ fileStats.push(`${normFile}:deleted`);
343
344
  }
344
345
  else if (e.code === 'EACCES' || e.code === 'EPERM') {
345
- fileStats.push(`${file}:inaccessible`);
346
+ fileStats.push(`${normFile}:inaccessible`);
346
347
  }
347
348
  else {
348
- fileStats.push(`${file}:error`);
349
+ fileStats.push(`${normFile}:error`);
349
350
  }
350
351
  }
351
352
  }
@@ -356,15 +357,17 @@ export async function generateWorkspaceFingerprint(workspaceRoot, relevantFiles)
356
357
  * Enforces LRU eviction to keep cache store below MAX_CACHE_SIZE_BYTES (5MB).
357
358
  */
358
359
  export function pruneCacheStore(store) {
360
+ if (!store || !store.entries)
361
+ return;
359
362
  let storeJson = JSON.stringify(store);
360
363
  while (Buffer.byteLength(storeJson, 'utf8') > MAX_CACHE_SIZE_BYTES) {
361
364
  const keys = Object.keys(store.entries);
362
365
  if (keys.length === 0)
363
366
  break;
364
367
  let oldestKey = keys[0];
365
- let oldestTime = store.entries[oldestKey].lastAccessedAt || store.entries[oldestKey].createdAt;
368
+ let oldestTime = store.entries[oldestKey].lastAccessedAt || store.entries[oldestKey].createdAt || 0;
366
369
  for (let i = 1; i < keys.length; i++) {
367
- const entryTime = store.entries[keys[i]].lastAccessedAt || store.entries[keys[i]].createdAt;
370
+ const entryTime = store.entries[keys[i]].lastAccessedAt || store.entries[keys[i]].createdAt || 0;
368
371
  if (entryTime < oldestTime) {
369
372
  oldestKey = keys[i];
370
373
  oldestTime = entryTime;
@@ -612,7 +615,14 @@ export async function invalidateFilesFromInvestigationCache(workspaceRoot, chang
612
615
  const entry = store.entries[hash];
613
616
  const dependsOnChange = entry.relevantFiles.some((f) => {
614
617
  const normF = f.replace(/\\/g, '/').toLowerCase();
615
- return normalizedChanged.some((c) => normF === c || normF.endsWith('/' + c) || c.endsWith('/' + normF));
618
+ const baseF = path.basename(normF);
619
+ return normalizedChanged.some((c) => {
620
+ const baseC = path.basename(c);
621
+ return (normF === c ||
622
+ normF.endsWith('/' + c) ||
623
+ c.endsWith('/' + normF) ||
624
+ (baseF === baseC && (normF.includes(c) || c.includes(normF))));
625
+ });
616
626
  });
617
627
  if (dependsOnChange) {
618
628
  delete store.entries[hash];
@@ -620,7 +630,7 @@ export async function invalidateFilesFromInvestigationCache(workspaceRoot, chang
620
630
  }
621
631
  }
622
632
  if (invalidated > 0) {
623
- writeCache(workspaceRoot, CACHE_FILE, store);
633
+ await writeCache(workspaceRoot, CACHE_FILE, store);
624
634
  debugLog(`[DEBUG] Investigation cache: invalidated ${invalidated} entries referencing changed files`);
625
635
  }
626
636
  }
@@ -73,6 +73,7 @@ export declare class ReadCache {
73
73
  /**
74
74
  * Invalidates and evicts all cache entries associated with a file path,
75
75
  * including full-file reads and any ranged or targeted sub-reads.
76
+ * Uses case-insensitive and normalized path comparison.
76
77
  *
77
78
  * @param filePath - The file path that was modified or deleted.
78
79
  */