tachibot-mcp 2.0.2

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 (214) hide show
  1. package/.env.example +260 -0
  2. package/CHANGELOG.md +54 -0
  3. package/CODE_OF_CONDUCT.md +56 -0
  4. package/CONTRIBUTING.md +54 -0
  5. package/Dockerfile +36 -0
  6. package/LICENSE +644 -0
  7. package/README.md +201 -0
  8. package/SECURITY.md +95 -0
  9. package/dist/personality/komaai-expressions.js +12 -0
  10. package/dist/profiles/balanced.json +33 -0
  11. package/dist/profiles/code_focus.json +33 -0
  12. package/dist/profiles/full.json +33 -0
  13. package/dist/profiles/minimal.json +33 -0
  14. package/dist/profiles/research_power.json +33 -0
  15. package/dist/scripts/build-profiles.js +46 -0
  16. package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
  17. package/dist/src/application/services/focus/FocusTool.service.js +109 -0
  18. package/dist/src/application/services/focus/ModeRegistry.js +46 -0
  19. package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
  20. package/dist/src/application/services/focus/modes/status.mode.js +50 -0
  21. package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
  22. package/dist/src/collaborative-orchestrator.js +391 -0
  23. package/dist/src/config/model-constants.js +188 -0
  24. package/dist/src/config/model-defaults.js +57 -0
  25. package/dist/src/config/model-preferences.js +382 -0
  26. package/dist/src/config/timeout-config.js +130 -0
  27. package/dist/src/config.js +173 -0
  28. package/dist/src/domain/interfaces/IFocusMode.js +5 -0
  29. package/dist/src/domain/interfaces/IProvider.js +6 -0
  30. package/dist/src/domain/interfaces/ITool.js +5 -0
  31. package/dist/src/focus-deep.js +245 -0
  32. package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
  33. package/dist/src/mcp-client.js +90 -0
  34. package/dist/src/memory/index.js +17 -0
  35. package/dist/src/memory/memory-config.js +135 -0
  36. package/dist/src/memory/memory-interface.js +174 -0
  37. package/dist/src/memory/memory-manager.js +383 -0
  38. package/dist/src/memory/providers/devlog-provider.js +385 -0
  39. package/dist/src/memory/providers/hybrid-provider.js +399 -0
  40. package/dist/src/memory/providers/local-provider.js +388 -0
  41. package/dist/src/memory/providers/mem0-provider.js +337 -0
  42. package/dist/src/modes/architect.js +477 -0
  43. package/dist/src/modes/auditor.js +362 -0
  44. package/dist/src/modes/challenger.js +841 -0
  45. package/dist/src/modes/code-reviewer.js +382 -0
  46. package/dist/src/modes/commit-guardian.js +424 -0
  47. package/dist/src/modes/documentation-writer.js +572 -0
  48. package/dist/src/modes/scout.js +587 -0
  49. package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
  50. package/dist/src/modes/shared/helpers/index.js +17 -0
  51. package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
  52. package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
  53. package/dist/src/modes/test-architect.js +767 -0
  54. package/dist/src/modes/verifier.js +378 -0
  55. package/dist/src/monitoring/performance-monitor.js +435 -0
  56. package/dist/src/optimization/batch-executor.js +121 -0
  57. package/dist/src/optimization/context-pruner.js +196 -0
  58. package/dist/src/optimization/cost-monitor.js +338 -0
  59. package/dist/src/optimization/index.js +65 -0
  60. package/dist/src/optimization/model-router.js +264 -0
  61. package/dist/src/optimization/result-cache.js +114 -0
  62. package/dist/src/optimization/token-optimizer.js +257 -0
  63. package/dist/src/optimization/token-tracker.js +118 -0
  64. package/dist/src/orchestrator-instructions.js +128 -0
  65. package/dist/src/orchestrator-lite.js +139 -0
  66. package/dist/src/orchestrator.js +191 -0
  67. package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
  68. package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
  69. package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
  70. package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
  71. package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
  72. package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
  73. package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
  74. package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
  75. package/dist/src/profiles/balanced.js +37 -0
  76. package/dist/src/profiles/code_focus.js +37 -0
  77. package/dist/src/profiles/debug_intensive.js +59 -0
  78. package/dist/src/profiles/full.js +37 -0
  79. package/dist/src/profiles/minimal.js +37 -0
  80. package/dist/src/profiles/research_code.js +59 -0
  81. package/dist/src/profiles/research_power.js +37 -0
  82. package/dist/src/profiles/types.js +5 -0
  83. package/dist/src/profiles/workflow_builder.js +53 -0
  84. package/dist/src/prompt-engineer-lite.js +78 -0
  85. package/dist/src/prompt-engineer.js +399 -0
  86. package/dist/src/reasoning-chain.js +508 -0
  87. package/dist/src/sequential-thinking.js +291 -0
  88. package/dist/src/server-diagnostic.js +74 -0
  89. package/dist/src/server-raw.js +158 -0
  90. package/dist/src/server-simple.js +58 -0
  91. package/dist/src/server.js +514 -0
  92. package/dist/src/session/session-logger.js +617 -0
  93. package/dist/src/session/session-manager.js +571 -0
  94. package/dist/src/session/session-tools.js +400 -0
  95. package/dist/src/tools/advanced-modes.js +200 -0
  96. package/dist/src/tools/claude-integration.js +356 -0
  97. package/dist/src/tools/consolidated/ai-router.js +174 -0
  98. package/dist/src/tools/consolidated/ai-tool.js +48 -0
  99. package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
  100. package/dist/src/tools/consolidated/environment-detector.js +80 -0
  101. package/dist/src/tools/consolidated/index.js +50 -0
  102. package/dist/src/tools/consolidated/search-tool.js +110 -0
  103. package/dist/src/tools/consolidated/workflow-tool.js +238 -0
  104. package/dist/src/tools/gemini-tools.js +329 -0
  105. package/dist/src/tools/grok-enhanced.js +376 -0
  106. package/dist/src/tools/grok-tools.js +299 -0
  107. package/dist/src/tools/lmstudio-tools.js +223 -0
  108. package/dist/src/tools/openai-tools.js +498 -0
  109. package/dist/src/tools/openrouter-tools.js +317 -0
  110. package/dist/src/tools/optimized-wrapper.js +204 -0
  111. package/dist/src/tools/perplexity-tools.js +294 -0
  112. package/dist/src/tools/pingpong-tool.js +343 -0
  113. package/dist/src/tools/qwen-wrapper.js +74 -0
  114. package/dist/src/tools/tool-router.js +444 -0
  115. package/dist/src/tools/unified-ai-provider.js +260 -0
  116. package/dist/src/tools/workflow-runner.js +425 -0
  117. package/dist/src/tools/workflow-validator-tool.js +107 -0
  118. package/dist/src/types.js +23 -0
  119. package/dist/src/utils/input-validator.js +130 -0
  120. package/dist/src/utils/model-router.js +91 -0
  121. package/dist/src/utils/progress-stream.js +255 -0
  122. package/dist/src/utils/provider-router.js +88 -0
  123. package/dist/src/utils/smart-api-client.js +146 -0
  124. package/dist/src/utils/table-builder.js +218 -0
  125. package/dist/src/utils/timestamp-formatter.js +134 -0
  126. package/dist/src/utils/tool-compressor.js +257 -0
  127. package/dist/src/utils/tool-config.js +201 -0
  128. package/dist/src/validators/dependency-graph-validator.js +147 -0
  129. package/dist/src/validators/interpolation-validator.js +222 -0
  130. package/dist/src/validators/output-usage-validator.js +151 -0
  131. package/dist/src/validators/syntax-validator.js +102 -0
  132. package/dist/src/validators/tool-registry-validator.js +123 -0
  133. package/dist/src/validators/tool-types.js +97 -0
  134. package/dist/src/validators/types.js +8 -0
  135. package/dist/src/validators/workflow-validator.js +134 -0
  136. package/dist/src/visualizer-lite.js +42 -0
  137. package/dist/src/visualizer.js +179 -0
  138. package/dist/src/workflows/circuit-breaker.js +199 -0
  139. package/dist/src/workflows/custom-workflows.js +451 -0
  140. package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
  141. package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
  142. package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
  143. package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
  144. package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
  145. package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
  146. package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
  147. package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
  148. package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
  149. package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
  150. package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
  151. package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
  152. package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
  153. package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
  154. package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
  155. package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
  156. package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
  157. package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
  158. package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
  159. package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
  160. package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
  161. package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
  162. package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
  163. package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
  164. package/dist/src/workflows/fallback-strategies.js +373 -0
  165. package/dist/src/workflows/message-queue.js +455 -0
  166. package/dist/src/workflows/model-router.js +189 -0
  167. package/dist/src/workflows/orchestrator-examples.js +174 -0
  168. package/dist/src/workflows/orchestrator-integration.js +200 -0
  169. package/dist/src/workflows/self-healing.js +524 -0
  170. package/dist/src/workflows/tool-mapper.js +407 -0
  171. package/dist/src/workflows/tool-orchestrator.js +796 -0
  172. package/dist/src/workflows/workflow-engine.js +573 -0
  173. package/dist/src/workflows/workflow-parser.js +283 -0
  174. package/dist/src/workflows/workflow-types.js +95 -0
  175. package/dist/src/workflows.js +568 -0
  176. package/dist/test-workflow-file-output.js +93 -0
  177. package/docs/API_KEYS.md +570 -0
  178. package/docs/CLAUDE_CODE_SETUP.md +181 -0
  179. package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
  180. package/docs/CONFIGURATION.md +745 -0
  181. package/docs/FOCUS_MODES.md +240 -0
  182. package/docs/INSTALLATION_BOTH.md +145 -0
  183. package/docs/TERMS.md +352 -0
  184. package/docs/TOOLS_REFERENCE.md +1622 -0
  185. package/docs/TOOL_PARAMETERS.md +496 -0
  186. package/docs/TOOL_PROFILES.md +236 -0
  187. package/docs/WORKFLOWS.md +987 -0
  188. package/docs/WORKFLOW_OUTPUT.md +198 -0
  189. package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
  190. package/docs/workflows/design-brainstorm.md +335 -0
  191. package/package.json +97 -0
  192. package/profiles/balanced.json +37 -0
  193. package/profiles/code_focus.json +37 -0
  194. package/profiles/debug_intensive.json +34 -0
  195. package/profiles/full.json +37 -0
  196. package/profiles/minimal.json +37 -0
  197. package/profiles/research_power.json +37 -0
  198. package/profiles/workflow_builder.json +37 -0
  199. package/smithery.yaml +66 -0
  200. package/start.sh +8 -0
  201. package/tools.config.json +81 -0
  202. package/tsconfig.json +18 -0
  203. package/workflows/accessibility-code-audit.yaml +92 -0
  204. package/workflows/code-architecture-review.yaml +202 -0
  205. package/workflows/code-review.yaml +142 -0
  206. package/workflows/core/iterative-problem-solver.yaml +283 -0
  207. package/workflows/creative-brainstorm-yaml.yaml +215 -0
  208. package/workflows/pingpong.yaml +141 -0
  209. package/workflows/system/README.md +412 -0
  210. package/workflows/system/challenger.yaml +175 -0
  211. package/workflows/system/scout.yaml +164 -0
  212. package/workflows/system/verifier.yaml +133 -0
  213. package/workflows/ultra-creative-brainstorm.yaml +318 -0
  214. package/workflows/ux-research-flow.yaml +92 -0
@@ -0,0 +1,587 @@
1
+ import { ModelRouter } from '../workflows/model-router.js';
2
+ import { getScoutModels, getDefaultModels } from '../config/model-defaults.js';
3
+ import { createProgressStream } from '../utils/progress-stream.js';
4
+ import { providerRouter } from '../utils/provider-router.js';
5
+ import { getSmartTimeout } from '../config/timeout-config.js';
6
+ import { execSync } from 'child_process';
7
+ // Provider name constants
8
+ const PROVIDER_PERPLEXITY = 'perplexity';
9
+ const PROVIDER_GROK = 'grok';
10
+ /**
11
+ * Get current date from system using bash
12
+ * Returns date in format: "October 20, 2025"
13
+ */
14
+ function getCurrentDate() {
15
+ try {
16
+ const dateStr = execSync('date "+%B %d, %Y"', { encoding: 'utf8' }).trim();
17
+ return dateStr;
18
+ }
19
+ catch (error) {
20
+ console.error('[Scout] Failed to get current date:', error);
21
+ // Fallback to JavaScript Date if bash fails
22
+ const now = new Date();
23
+ return now.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
24
+ }
25
+ }
26
+ export class Scout {
27
+ constructor() {
28
+ this.variants = {
29
+ 'research_scout': {
30
+ flow: 'perplexity-first-always',
31
+ perplexityTimeout: 65000, // Base timeout (kept for backwards compat)
32
+ parallelModels: [], // Set in constructor from config
33
+ tokens: 2500,
34
+ defaultProvider: PROVIDER_PERPLEXITY,
35
+ priority: 'batch',
36
+ providers: [PROVIDER_PERPLEXITY, PROVIDER_GROK]
37
+ },
38
+ 'code_scout': {
39
+ flow: 'conditional-hybrid',
40
+ perplexityTimeout: 8000, // Base timeout
41
+ perplexityFor: 'latest API docs only',
42
+ primary: 'gemini-2.5-flash',
43
+ upgradeCondition: 'complexity > 0.8',
44
+ tokens: 2000,
45
+ defaultProvider: PROVIDER_PERPLEXITY,
46
+ priority: 'interactive',
47
+ providers: [PROVIDER_PERPLEXITY, PROVIDER_GROK]
48
+ },
49
+ 'fact_scout': {
50
+ flow: 'waterfall',
51
+ perplexityTimeout: 8000, // Base timeout
52
+ tokens: 1500,
53
+ defaultProvider: PROVIDER_PERPLEXITY,
54
+ priority: 'batch',
55
+ providers: [PROVIDER_PERPLEXITY, PROVIDER_GROK]
56
+ },
57
+ 'quick_scout': {
58
+ flow: 'conditional-hybrid',
59
+ perplexityTimeout: 20000, // Base timeout
60
+ parallelModels: [], // Set in constructor from config
61
+ tokens: 1000,
62
+ defaultProvider: PROVIDER_PERPLEXITY,
63
+ priority: 'interactive',
64
+ providers: [PROVIDER_PERPLEXITY, PROVIDER_GROK]
65
+ }
66
+ };
67
+ // Load configuration from environment
68
+ this.defaultSearchProvider = process.env.DEFAULT_SEARCH_PROVIDER || 'perplexity';
69
+ this.grokApiKey = process.env.GROK_API_KEY || process.env.XAI_API_KEY;
70
+ this.perplexityApiKey = process.env.PERPLEXITY_API_KEY;
71
+ this.modelRouter = new ModelRouter();
72
+ // Load model configurations
73
+ const scoutModels = getScoutModels();
74
+ this.variants['research_scout'].parallelModels = scoutModels.research;
75
+ this.variants['quick_scout'].parallelModels = scoutModels.quick;
76
+ }
77
+ async scout(query, options = {}) {
78
+ const startTime = Date.now();
79
+ const variant = this.getVariant(options.variant || 'research_scout');
80
+ const maxTokens = options.maxTokens || variant.tokens;
81
+ // Get current date and prepend to query for temporal context
82
+ const currentDate = getCurrentDate();
83
+ const dateAwareQuery = `[Current date: ${currentDate}] ${query}`;
84
+ console.log(`[Scout] Date-aware query: ${dateAwareQuery}`);
85
+ // Create progress stream
86
+ const progressStream = createProgressStream(4);
87
+ progressStream.start(`Scout (${options.variant || 'research_scout'})`);
88
+ // Determine search provider
89
+ const searchProvider = options.searchProvider || variant.defaultProvider || this.defaultSearchProvider;
90
+ // Cost control for Grok live search
91
+ const maxSearchSources = options.maxSearchSources || this.getSourceLimit(options.variant || 'research_scout');
92
+ let result = {
93
+ synthesis: '',
94
+ executionTime: 0,
95
+ tokensUsed: 0
96
+ };
97
+ try {
98
+ progressStream.step(`Executing ${variant.flow} flow...`, 1);
99
+ switch (variant.flow) {
100
+ case 'perplexity-first-always':
101
+ result = await this.perplexityFirstFlow(dateAwareQuery, variant, maxTokens, progressStream);
102
+ break;
103
+ case 'conditional-hybrid':
104
+ result = await this.conditionalHybridFlow(dateAwareQuery, variant, maxTokens, searchProvider, undefined, progressStream);
105
+ break;
106
+ case 'waterfall':
107
+ result = await this.waterfallFlow(dateAwareQuery, variant, maxTokens, progressStream);
108
+ break;
109
+ }
110
+ progressStream.complete(`Scout complete: ${result.facts?.facts.length || 0} facts, ${result.analyses?.length || 0} analyses`);
111
+ }
112
+ catch (error) {
113
+ console.error('Scout error:', error);
114
+ progressStream.error(`Scout error: ${error instanceof Error ? error.message : String(error)}`);
115
+ result.warning = 'Scout encountered an error, falling back to basic analysis';
116
+ result.synthesis = await this.fallbackAnalysis(query, maxTokens);
117
+ }
118
+ result.executionTime = Date.now() - startTime;
119
+ return result;
120
+ }
121
+ async conditionalHybridFlow(query, variant, maxTokens, searchProvider = 'perplexity', grokOptions, progressStream) {
122
+ if (progressStream)
123
+ progressStream.step(`Searching with ${searchProvider}...`, 2);
124
+ const probe = this.craftTargetedProbe(query);
125
+ const facts = await this.quickPerplexityScan(probe, {
126
+ timeout: variant.perplexityTimeout,
127
+ maxTokens: 500
128
+ }, searchProvider, grokOptions, variant.priority || 'interactive');
129
+ if (facts && this.validateFacts(facts)) {
130
+ if (progressStream)
131
+ progressStream.step(`Analyzing with ${variant.parallelModels?.length || 'multiple'} models...`, 3);
132
+ const context = this.parseToContext(facts);
133
+ const parallelModels = variant.parallelModels || getDefaultModels();
134
+ const analyses = await Promise.all(parallelModels.map(model => this.analyzeWithContext(model, query, context, maxTokens / parallelModels.length)));
135
+ return {
136
+ probe: {
137
+ query: probe,
138
+ response: facts.response || '',
139
+ hasUsefulFacts: true,
140
+ confidence: facts.reliability || 0.8
141
+ },
142
+ facts: facts,
143
+ analyses,
144
+ synthesis: await this.synthesize(facts, analyses),
145
+ tokensUsed: this.estimateTokens(facts, analyses),
146
+ executionTime: 0
147
+ };
148
+ }
149
+ const deepFacts = await this.deepPerplexitySearch(query, {
150
+ timeout: 2000,
151
+ maxTokens: 1500
152
+ }, variant.priority || 'batch');
153
+ if (!deepFacts) {
154
+ const results = await this.parallelWithoutFacts(query, variant, maxTokens);
155
+ return {
156
+ ...results,
157
+ warning: 'May contain outdated information - no current facts available'
158
+ };
159
+ }
160
+ const analyses = await this.parallelAnalysis(query, deepFacts, variant, maxTokens);
161
+ return {
162
+ facts: deepFacts,
163
+ analyses,
164
+ synthesis: await this.synthesize(deepFacts, analyses),
165
+ tokensUsed: this.estimateTokens(deepFacts, analyses),
166
+ executionTime: 0
167
+ };
168
+ }
169
+ async perplexityFirstFlow(query, variant, maxTokens, progressStream) {
170
+ if (progressStream)
171
+ progressStream.step(`Searching with Perplexity...`, 2);
172
+ const facts = await this.quickPerplexityScan(query, {
173
+ timeout: variant.perplexityTimeout,
174
+ maxTokens: Math.min(1000, maxTokens / 3)
175
+ }, 'perplexity', undefined, variant.priority || 'batch');
176
+ if (!facts) {
177
+ return {
178
+ warning: 'Perplexity timeout or error',
179
+ synthesis: `Unable to retrieve information within timeout period (${variant.perplexityTimeout}ms)`,
180
+ executionTime: 0,
181
+ tokensUsed: 0
182
+ };
183
+ }
184
+ if (progressStream)
185
+ progressStream.step(`Parallel analysis with ${variant.parallelModels?.length || 'multiple'} models...`, 3);
186
+ const analyses = await this.parallelAnalysis(query, facts, variant, maxTokens);
187
+ return {
188
+ facts,
189
+ analyses,
190
+ synthesis: await this.synthesize(facts, analyses),
191
+ tokensUsed: this.estimateTokens(facts, analyses),
192
+ executionTime: 0
193
+ };
194
+ }
195
+ async waterfallFlow(query, variant, maxTokens, progressStream) {
196
+ const startTime = Date.now();
197
+ try {
198
+ if (progressStream)
199
+ progressStream.step(`Deep search with Perplexity...`, 2);
200
+ const facts = await this.deepPerplexitySearch(query, {
201
+ timeout: variant.perplexityTimeout * 2,
202
+ maxTokens: maxTokens / 2
203
+ }, variant.priority || 'batch');
204
+ if (!facts) {
205
+ console.warn(`Waterfall flow: No facts retrieved for query: ${query}`);
206
+ return {
207
+ warning: 'No facts retrieved',
208
+ synthesis: 'Unable to complete waterfall flow',
209
+ executionTime: Date.now() - startTime,
210
+ tokensUsed: 0
211
+ };
212
+ }
213
+ if (progressStream)
214
+ progressStream.step(`Verifying facts...`, 3);
215
+ const verification = await this.verifyFacts(facts);
216
+ const synthesis = await this.synthesizeWaterfall(facts, verification);
217
+ const executionTime = Date.now() - startTime;
218
+ return {
219
+ facts,
220
+ synthesis,
221
+ tokensUsed: this.estimateTokens(facts),
222
+ executionTime
223
+ };
224
+ }
225
+ catch (error) {
226
+ console.error('Waterfall flow failed:', error);
227
+ return {
228
+ warning: `Waterfall flow failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
229
+ synthesis: 'Unable to complete waterfall flow',
230
+ executionTime: Date.now() - startTime,
231
+ tokensUsed: 0
232
+ };
233
+ }
234
+ }
235
+ craftTargetedProbe(query) {
236
+ const keywords = this.extractKeywords(query);
237
+ const questionType = this.identifyQuestionType(query);
238
+ if (questionType === 'latest') {
239
+ return `Latest information about ${keywords.join(' ')} as of 2025`;
240
+ }
241
+ else if (questionType === 'technical') {
242
+ return `Technical documentation and API details for ${keywords.join(' ')}`;
243
+ }
244
+ else if (questionType === 'comparison') {
245
+ return `Compare ${keywords.join(' vs ')} with current data`;
246
+ }
247
+ return query;
248
+ }
249
+ getSourceLimit(variant) {
250
+ // Cost control: limit sources based on variant
251
+ switch (variant) {
252
+ case 'quick_scout':
253
+ return 50;
254
+ case 'fact_scout':
255
+ return 150;
256
+ case 'research_scout':
257
+ return parseInt(process.env.GROK_SEARCH_SOURCES_LIMIT || '100');
258
+ default:
259
+ return 100;
260
+ }
261
+ }
262
+ async quickPerplexityScan(probe, options, searchProvider = 'perplexity', grokOptions, priority = 'interactive') {
263
+ try {
264
+ // Build provider list for failover
265
+ const providers = [];
266
+ // Add Perplexity if available
267
+ if ((searchProvider === PROVIDER_PERPLEXITY || searchProvider === 'both') && this.perplexityApiKey) {
268
+ providers.push({
269
+ name: PROVIDER_PERPLEXITY,
270
+ callable: () => this.queryPerplexity(probe, options.maxTokens),
271
+ enabled: true,
272
+ priority: 1
273
+ });
274
+ }
275
+ // Add Grok if available
276
+ if ((searchProvider === PROVIDER_GROK || searchProvider === 'both') && this.grokApiKey) {
277
+ providers.push({
278
+ name: PROVIDER_GROK,
279
+ callable: async () => {
280
+ const result = await this.queryGrokWithLiveSearch(probe, options.maxTokens, grokOptions);
281
+ return result || '';
282
+ },
283
+ enabled: true,
284
+ priority: 2
285
+ });
286
+ }
287
+ if (providers.length === 0) {
288
+ console.warn('[Scout] No search providers available');
289
+ return null;
290
+ }
291
+ // Get smart timeout config
292
+ const timeoutConfig = getSmartTimeout(PROVIDER_PERPLEXITY, priority);
293
+ // Use ProviderRouter with SmartAPIClient for failover + retries
294
+ const result = await providerRouter.route(providers, { query: probe }, {
295
+ provider: PROVIDER_PERPLEXITY,
296
+ priority,
297
+ baseTimeoutMs: timeoutConfig.base,
298
+ maxTimeoutMs: timeoutConfig.max,
299
+ maxRetries: timeoutConfig.retries
300
+ });
301
+ // Null safety check
302
+ if (!result?.result) {
303
+ console.warn('[Scout] No result returned from provider router');
304
+ return null;
305
+ }
306
+ // Extract and validate facts
307
+ const facts = this.extractFacts(result.result);
308
+ if (!facts || facts.length === 0) {
309
+ console.warn('[Scout] No facts extracted from search response');
310
+ return null;
311
+ }
312
+ return {
313
+ facts,
314
+ sources: this.extractSources(result.result) || [],
315
+ timestamp: new Date().toISOString(),
316
+ reliability: this.assessReliability(result.result)
317
+ };
318
+ }
319
+ catch (error) {
320
+ console.error('[Scout] Error in quickPerplexityScan:', error instanceof Error ? error.message : String(error));
321
+ return null;
322
+ }
323
+ }
324
+ async deepPerplexitySearch(query, options, priority = 'batch') {
325
+ const startTime = Date.now();
326
+ try {
327
+ const enhancedQuery = `Provide comprehensive, current information about: ${query}. Include latest updates, key facts, and reliable sources.`;
328
+ console.log(`[Scout] Starting deep search for query: ${query}`);
329
+ // Build provider list for failover
330
+ const providers = [];
331
+ if (this.perplexityApiKey) {
332
+ providers.push({
333
+ name: PROVIDER_PERPLEXITY,
334
+ callable: () => this.queryPerplexity(enhancedQuery, options.maxTokens),
335
+ enabled: true,
336
+ priority: 1
337
+ });
338
+ }
339
+ if (this.grokApiKey) {
340
+ providers.push({
341
+ name: PROVIDER_GROK,
342
+ callable: async () => {
343
+ const result = await this.queryGrokWithLiveSearch(enhancedQuery, options.maxTokens);
344
+ return result || '';
345
+ },
346
+ enabled: true,
347
+ priority: 2
348
+ });
349
+ }
350
+ if (providers.length === 0) {
351
+ console.warn('[Scout] No search providers available for deep search');
352
+ return null;
353
+ }
354
+ // Get smart timeout config
355
+ const timeoutConfig = getSmartTimeout(PROVIDER_PERPLEXITY, priority);
356
+ // Use ProviderRouter with SmartAPIClient
357
+ const result = await providerRouter.route(providers, { query: enhancedQuery }, {
358
+ provider: PROVIDER_PERPLEXITY,
359
+ priority,
360
+ baseTimeoutMs: timeoutConfig.base,
361
+ maxTimeoutMs: timeoutConfig.max,
362
+ maxRetries: timeoutConfig.retries
363
+ });
364
+ // Null safety check
365
+ if (!result?.result) {
366
+ console.warn('[Scout] Deep search returned empty response');
367
+ return null;
368
+ }
369
+ // Extract and validate facts
370
+ const facts = this.extractFacts(result.result);
371
+ if (!facts || facts.length === 0) {
372
+ console.warn('[Scout] No facts extracted from deep search response');
373
+ return null;
374
+ }
375
+ const searchDuration = Date.now() - startTime;
376
+ console.log(`[Scout] Deep search completed in ${searchDuration}ms using ${result.provider}`);
377
+ return {
378
+ facts,
379
+ sources: this.extractSources(result.result) || [],
380
+ timestamp: new Date().toISOString(),
381
+ reliability: this.assessReliability(result.result)
382
+ };
383
+ }
384
+ catch (error) {
385
+ const duration = Date.now() - startTime;
386
+ console.error(`[Scout] Error in deepPerplexitySearch after ${duration}ms:`, error instanceof Error ? error.message : String(error));
387
+ return null;
388
+ }
389
+ }
390
+ validateFacts(facts) {
391
+ return facts.facts.length > 0 && facts.reliability > 0.5;
392
+ }
393
+ parseToContext(facts) {
394
+ return `Context (${facts.timestamp}):\n${facts.facts.join('\n')}\n\nSources: ${facts.sources?.join(', ') || 'Not specified'}`;
395
+ }
396
+ async analyzeWithContext(model, query, context, maxTokens) {
397
+ const prompt = `${context}\n\nAnalyze: ${query}`;
398
+ const response = await this.queryModel(model, prompt, maxTokens);
399
+ return {
400
+ model,
401
+ analysis: response,
402
+ insights: this.extractInsights(response)
403
+ };
404
+ }
405
+ async parallelAnalysis(query, facts, variant, maxTokens) {
406
+ const models = variant.parallelModels || getDefaultModels();
407
+ const context = this.parseToContext(facts);
408
+ const analyses = await Promise.all(models.map(model => this.analyzeWithContext(model, query, context, maxTokens / models.length)));
409
+ return analyses;
410
+ }
411
+ async parallelWithoutFacts(query, variant, maxTokens) {
412
+ const models = variant.parallelModels || getDefaultModels();
413
+ const analyses = await Promise.all(models.map(async (model) => {
414
+ const response = await this.queryModel(model, query, maxTokens / models.length);
415
+ return {
416
+ model,
417
+ analysis: response,
418
+ insights: this.extractInsights(response)
419
+ };
420
+ }));
421
+ return {
422
+ analyses,
423
+ synthesis: await this.synthesizeWithoutFacts(analyses),
424
+ tokensUsed: this.estimateTokens(null, analyses),
425
+ executionTime: 0
426
+ };
427
+ }
428
+ async synthesize(facts, analyses) {
429
+ let synthesis = '';
430
+ if (facts && facts.facts.length > 0) {
431
+ synthesis += `Current Information (${facts.timestamp}):\n`;
432
+ facts.facts.slice(0, 3).forEach(fact => synthesis += `• ${fact}\n`);
433
+ synthesis += '\n';
434
+ }
435
+ synthesis += 'Analysis:\n';
436
+ analyses.forEach(analysis => {
437
+ synthesis += `${analysis.model}:\n`;
438
+ analysis.insights.slice(0, 2).forEach(insight => synthesis += `• ${insight}\n`);
439
+ });
440
+ return synthesis;
441
+ }
442
+ async synthesizeWithoutFacts(analyses) {
443
+ let synthesis = 'Multi-Model Analysis:\n\n';
444
+ analyses.forEach(analysis => {
445
+ synthesis += `${analysis.model}:\n`;
446
+ analysis.insights.slice(0, 3).forEach(insight => synthesis += `• ${insight}\n`);
447
+ synthesis += '\n';
448
+ });
449
+ return synthesis;
450
+ }
451
+ async synthesizeWaterfall(facts, verification) {
452
+ return `Verified Information:\n${facts.facts.join('\n')}\n\nReliability: ${facts.reliability}`;
453
+ }
454
+ async verifyFacts(facts) {
455
+ return { verified: true, confidence: facts.reliability };
456
+ }
457
+ async fallbackAnalysis(query, maxTokens) {
458
+ return `Analysis of: ${query}\n[Fallback mode - limited information available]`;
459
+ }
460
+ async queryPerplexity(query, maxTokens) {
461
+ // Use existing Perplexity tool instead of duplicating API logic
462
+ if (!this.perplexityApiKey) {
463
+ throw new Error('Perplexity API key not configured');
464
+ }
465
+ try {
466
+ const { callPerplexity, PerplexityModel } = await import('../tools/perplexity-tools.js');
467
+ const messages = [{ role: 'user', content: query }];
468
+ // Call the existing Perplexity tool
469
+ const result = await callPerplexity(messages, PerplexityModel.SONAR_PRO);
470
+ // Check if result is an error message
471
+ if (result.startsWith('[Perplexity')) {
472
+ throw new Error(result);
473
+ }
474
+ return result;
475
+ }
476
+ catch (error) {
477
+ console.error('Scout queryPerplexity error:', error);
478
+ throw error; // Re-throw so Promise.race timeout can catch it properly
479
+ }
480
+ }
481
+ async queryGrokWithLiveSearch(query, maxTokens, options) {
482
+ if (!this.grokApiKey) {
483
+ return null;
484
+ }
485
+ try {
486
+ // Import Grok enhanced API
487
+ const { callGrokEnhanced } = await import('../tools/grok-enhanced.js');
488
+ const messages = [{ role: 'user', content: query }];
489
+ const result = await callGrokEnhanced(messages, {
490
+ model: 'grok-4-0709',
491
+ maxTokens,
492
+ enableLiveSearch: options?.enableLiveSearch ?? true,
493
+ searchSources: options?.maxSources ?? 100,
494
+ searchDomains: options?.domains
495
+ });
496
+ return result.content;
497
+ }
498
+ catch (error) {
499
+ console.error('Grok live search error:', error);
500
+ return null;
501
+ }
502
+ }
503
+ combineSearchResults(perplexityResult, grokResult) {
504
+ // Intelligently combine results from both providers
505
+ return `## Perplexity Results:\n${perplexityResult}\n\n## Grok Live Search Results:\n${grokResult}`;
506
+ }
507
+ async queryModel(model, prompt, maxTokens) {
508
+ try {
509
+ const { modelRouter } = await import('../utils/model-router.js');
510
+ const response = await modelRouter.callModel({
511
+ model,
512
+ prompt,
513
+ maxTokens,
514
+ temperature: 0.7
515
+ });
516
+ return response.content;
517
+ }
518
+ catch (error) {
519
+ console.error(`Scout model query error (${model}):`, error);
520
+ throw new Error(`Failed to analyze with ${model}: ${error.message}`);
521
+ }
522
+ }
523
+ extractKeywords(query) {
524
+ const words = query.toLowerCase().split(/\s+/);
525
+ const stopWords = ['what', 'how', 'why', 'when', 'where', 'is', 'are', 'the', 'a', 'an'];
526
+ return words.filter(w => !stopWords.includes(w) && w.length > 2);
527
+ }
528
+ identifyQuestionType(query) {
529
+ const lower = query.toLowerCase();
530
+ if (lower.includes('latest') || lower.includes('current') || lower.includes('new'))
531
+ return 'latest';
532
+ if (lower.includes('api') || lower.includes('function') || lower.includes('method'))
533
+ return 'technical';
534
+ if (lower.includes('vs') || lower.includes('compare') || lower.includes('difference'))
535
+ return 'comparison';
536
+ return 'general';
537
+ }
538
+ extractFacts(response) {
539
+ const lines = response.split('\n');
540
+ return lines
541
+ .filter(line => line.trim().length > 20)
542
+ .slice(0, 5)
543
+ .map(line => line.trim());
544
+ }
545
+ extractSources(response) {
546
+ const sourcePattern = /\[(\d+)\]\s*([^\n]+)/g;
547
+ const sources = [];
548
+ let match;
549
+ while ((match = sourcePattern.exec(response)) !== null) {
550
+ sources.push(match[2]);
551
+ }
552
+ return sources;
553
+ }
554
+ extractInsights(response) {
555
+ const lines = response.split('\n');
556
+ return lines
557
+ .filter(line => line.includes('insight') || line.includes('Key') || line.startsWith('•'))
558
+ .map(line => line.replace(/^[•\-]\s*/, '').trim())
559
+ .filter(line => line.length > 10)
560
+ .slice(0, 3);
561
+ }
562
+ assessReliability(response) {
563
+ let score = 0.5;
564
+ if (response.includes('source') || response.includes('['))
565
+ score += 0.2;
566
+ if (response.includes('2025') || response.includes('2024'))
567
+ score += 0.1;
568
+ if (response.includes('verified') || response.includes('confirmed'))
569
+ score += 0.1;
570
+ if (response.length > 500)
571
+ score += 0.1;
572
+ return Math.min(score, 1.0);
573
+ }
574
+ estimateTokens(facts, analyses) {
575
+ let tokens = 0;
576
+ if (facts) {
577
+ tokens += facts.facts.join('').length / 4;
578
+ }
579
+ if (analyses) {
580
+ tokens += analyses.reduce((sum, a) => sum + a.analysis.length / 4, 0);
581
+ }
582
+ return Math.round(tokens);
583
+ }
584
+ getVariant(name) {
585
+ return this.variants[name] || this.variants['research_scout'];
586
+ }
587
+ }