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,841 @@
1
+ /**
2
+ * Challenger - Functional TypeScript Implementation
3
+ *
4
+ * Purpose: Devil's advocate tool that expands perspectives and finds hidden paths.
5
+ * Not a "fault finder" but a "perspective expander" that challenges assumptions.
6
+ *
7
+ * Flow:
8
+ * 1. Extract claims
9
+ * 2. Generate counter-arguments
10
+ * 3. Research alternative perspectives (Perplexity)
11
+ * 4. Find counter-evidence (Grok)
12
+ * 5. Multi-model verification (optional, for high-severity only)
13
+ * 6. Flag uncontested tone (warning only)
14
+ * 7. Find third way (creative alternatives)
15
+ * 8. Explore opposite (what if we're backwards?)
16
+ * 9. Synthesize report
17
+ */
18
+ import { ModelRouter } from '../workflows/model-router.js';
19
+ import { getChallengerModels } from '../config/model-defaults.js';
20
+ import { createProgressStream } from '../utils/progress-stream.js';
21
+ import { smartAPIClient } from '../utils/smart-api-client.js';
22
+ import { getSmartTimeout } from '../config/timeout-config.js';
23
+ import { execSync } from 'child_process';
24
+ // ============================================
25
+ // CONSTANTS
26
+ // ============================================
27
+ const PROVIDER_PERPLEXITY = 'perplexity';
28
+ const PROVIDER_GROK = 'grok';
29
+ const DEFAULT_CONFIG = {
30
+ model: 'gpt-5-mini',
31
+ maxTokens: 2000,
32
+ temperature: 0.9,
33
+ thoroughness: 'standard'
34
+ };
35
+ // ============================================
36
+ // PURE UTILITY FUNCTIONS
37
+ // ============================================
38
+ /**
39
+ * Get current date from system using bash
40
+ * Returns date in format: "October 20, 2025"
41
+ */
42
+ const getCurrentDate = () => {
43
+ try {
44
+ const dateStr = execSync('date "+%B %d, %Y"', { encoding: 'utf8' }).trim();
45
+ return dateStr;
46
+ }
47
+ catch (error) {
48
+ console.error('[Challenger] Failed to get current date:', error);
49
+ const now = new Date();
50
+ return now.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
51
+ }
52
+ };
53
+ const contextToText = (context) => typeof context === 'string' ? context : JSON.stringify(context, null, 2);
54
+ // ============================================
55
+ // PURE FUNCTIONS - CORE LOGIC
56
+ // ============================================
57
+ /**
58
+ * Create challenger context with defaults
59
+ */
60
+ const createContext = (options) => ({
61
+ config: {
62
+ model: options.model ?? DEFAULT_CONFIG.model,
63
+ maxTokens: options.maxTokens ?? DEFAULT_CONFIG.maxTokens,
64
+ temperature: options.temperature ?? DEFAULT_CONFIG.temperature,
65
+ thoroughness: options.thoroughness ?? DEFAULT_CONFIG.thoroughness,
66
+ enableFactChecking: options.enableFactChecking ?? true,
67
+ enableDocVerification: options.enableDocVerification ?? true,
68
+ enableMultiModelVerification: options.enableMultiModelVerification ?? true
69
+ },
70
+ modelRouter: new ModelRouter(),
71
+ currentDate: getCurrentDate(),
72
+ thinkingProcess: []
73
+ });
74
+ /**
75
+ * Log thinking process (side effect contained)
76
+ */
77
+ const addThought = (ctx, thought) => {
78
+ ctx.thinkingProcess.push(thought);
79
+ if (process.env.DEBUG === 'true') {
80
+ console.error(`[CHALLENGER] ${thought}`);
81
+ }
82
+ };
83
+ /**
84
+ * Flag uncontested tone in text
85
+ * Detects authoritarian language that shuts down debate
86
+ */
87
+ const flagUncontestedTone = (text) => {
88
+ const uncontestedPhrases = [
89
+ 'obviously', 'clearly', 'everyone knows', 'everyone agrees',
90
+ 'the only way', 'undeniably', 'without question', 'unanimous',
91
+ 'no disagreement', 'settled science', 'beyond doubt'
92
+ ];
93
+ const foundPhrases = uncontestedPhrases.filter(phrase => text.toLowerCase().includes(phrase));
94
+ const severity = foundPhrases.length >= 3 ? 'high'
95
+ : foundPhrases.length >= 2 ? 'medium'
96
+ : 'low';
97
+ const message = foundPhrases.length === 0
98
+ ? 'No uncontested tone detected - healthy room for debate'
99
+ : `Detected ${foundPhrases.length} authoritarian phrase(s) that may shut down dissent`;
100
+ return {
101
+ detected: foundPhrases.length > 0,
102
+ phrases: foundPhrases,
103
+ severity,
104
+ message
105
+ };
106
+ };
107
+ /**
108
+ * Extract claims from text
109
+ */
110
+ const extractClaims = (text) => {
111
+ const sentences = text
112
+ .split(/[.!?]+/)
113
+ .filter(s => s.trim().length > 10)
114
+ .map(s => s.trim());
115
+ const claims = sentences
116
+ .slice(0, 10)
117
+ .map((sentence, index) => analyzeSentence(sentence, index))
118
+ .filter((claim) => claim !== null);
119
+ return claims;
120
+ };
121
+ /**
122
+ * Analyze sentence to determine claim type
123
+ */
124
+ const analyzeSentence = (sentence, index) => {
125
+ const patterns = {
126
+ fact: ['is', 'are', 'was', 'were', 'has', 'have', 'will'],
127
+ opinion: ['believe', 'think', 'feel', 'seems', 'appears', 'might'],
128
+ assumption: ['assume', 'presumably', 'probably', 'likely'],
129
+ conclusion: ['therefore', 'thus', 'hence', 'so', 'conclusion']
130
+ };
131
+ const lowerSentence = sentence.toLowerCase();
132
+ const type = Object.entries(patterns).find(([_, indicators]) => indicators.some(indicator => lowerSentence.includes(indicator)))?.[0] ?? 'fact';
133
+ const confidence = type === 'fact' ? 0.7
134
+ : type === 'conclusion' ? 0.6
135
+ : type === 'assumption' ? 0.4
136
+ : 0.3;
137
+ return {
138
+ id: `claim-${index}`,
139
+ text: sentence,
140
+ type,
141
+ confidence
142
+ };
143
+ };
144
+ // Export class for backward compatibility with MCP tool registration
145
+ export class Challenger {
146
+ constructor() {
147
+ this.defaultModel = 'gpt-5-mini';
148
+ this.defaultMaxTokens = 2000;
149
+ this.defaultTemperature = 0.9;
150
+ this.thinkingProcess = [];
151
+ this.modelRouter = new ModelRouter();
152
+ }
153
+ async challenge(context, options = {}) {
154
+ const model = options.model || this.defaultModel;
155
+ const maxTokens = options.maxTokens || this.defaultMaxTokens;
156
+ const temperature = options.temperature || this.defaultTemperature;
157
+ const thoroughness = options.thoroughness || 'standard';
158
+ const enableFactChecking = options.enableFactChecking !== false; // Default true
159
+ const enableDocVerification = options.enableDocVerification !== false; // Default true
160
+ const enableMultiModelVerification = options.enableMultiModelVerification !== false; // Default true
161
+ // Reset thinking process
162
+ this.thinkingProcess = [];
163
+ // Create progress stream
164
+ const totalSteps = 6 + (enableFactChecking ? 1 : 0) + (enableDocVerification ? 1 : 0) + (enableMultiModelVerification ? 1 : 0);
165
+ const progressStream = createProgressStream(totalSteps);
166
+ progressStream.start(`Challenger (${thoroughness} mode)`);
167
+ // Step 1: Extract claims with sequential thinking
168
+ progressStream.step(`Extracting claims from context...`, 1);
169
+ await this.think("Starting critical analysis. First, I'll extract all claims from the provided context...");
170
+ const claims = await this.extractClaims(context, 500);
171
+ await this.think(`Extracted ${claims.length} claims. Types: ${claims.filter(c => c.type === 'fact').length} facts, ${claims.filter(c => c.type === 'opinion').length} opinions, ${claims.filter(c => c.type === 'assumption').length} assumptions, ${claims.filter(c => c.type === 'conclusion').length} conclusions.`);
172
+ // Step 2: Generate challenges
173
+ progressStream.step(`Generating counter-arguments...`, 2);
174
+ await this.think("Now generating counter-arguments for each claim using devil's advocate approach...");
175
+ const challenges = await this.generateChallenges(claims, model, maxTokens, temperature);
176
+ await this.think(`Generated ${challenges.length} challenges. ${challenges.filter(c => c.severity === 'high').length} high severity, ${challenges.filter(c => c.severity === 'medium').length} medium severity.`);
177
+ // Step 3: Fact-checking (Perplexity) for fact-type claims
178
+ let factCheckResults;
179
+ let currentStep = 3;
180
+ if (enableFactChecking && thoroughness !== 'quick') {
181
+ progressStream.step(`Fact-checking with Perplexity...`, currentStep++);
182
+ await this.think("Verifying factual claims using Perplexity search with up-to-date information...");
183
+ factCheckResults = await this.verifyFactsWithPerplexity(claims, thoroughness);
184
+ await this.think(`Fact-checked ${factCheckResults.length} claims. ${factCheckResults.filter(r => r.verified).length} verified, ${factCheckResults.filter(r => !r.verified).length} disputed.`);
185
+ }
186
+ // Step 4: Documentation verification (Grok) for technical claims
187
+ let docVerificationResults;
188
+ if (enableDocVerification && thoroughness === 'deep') {
189
+ progressStream.step(`Verifying documentation with Grok...`, currentStep++);
190
+ await this.think("Cross-verifying technical claims against official documentation using Grok search...");
191
+ docVerificationResults = await this.verifyDocsWithGrok(claims);
192
+ await this.think(`Checked ${docVerificationResults.length} claims against official docs. ${docVerificationResults.filter(r => r.docsFound).length} found supporting documentation.`);
193
+ }
194
+ // Step 5: Multi-model verification (Verifier) for high-severity challenges
195
+ let multiModelConsensus;
196
+ if (enableMultiModelVerification && thoroughness === 'deep') {
197
+ const highSeverityChallenges = challenges.filter(c => c.severity === 'high');
198
+ if (highSeverityChallenges.length > 0) {
199
+ progressStream.step(`Multi-model consensus on high-severity challenges...`, currentStep++);
200
+ await this.think(`Running multi-model verification on ${highSeverityChallenges.length} high-severity challenges for consensus...`);
201
+ multiModelConsensus = await this.getMultiModelConsensus(highSeverityChallenges, claims);
202
+ await this.think("Multi-model verification complete. Consensus analysis integrated.");
203
+ }
204
+ }
205
+ // Step 6: Detect groupthink
206
+ progressStream.step(`Analyzing for echo chamber effects...`, currentStep++);
207
+ await this.think("Analyzing input for echo chamber effects and groupthink patterns...");
208
+ const groupthinkDetected = this.detectGroupthink(context);
209
+ await this.think(`Groupthink risk: ${groupthinkDetected ? 'HIGH' : 'LOW'}. ${groupthinkDetected ? 'Echo chamber detected - diverse perspectives needed.' : 'Healthy diversity of thought detected.'}`);
210
+ // Step 7: Generate alternatives
211
+ progressStream.step(`Generating alternative perspectives...`, currentStep++);
212
+ await this.think("Generating alternative perspectives to challenge dominant narratives...");
213
+ const alternativePerspectives = await this.generateAlternatives(claims, challenges, model);
214
+ await this.think(`Generated ${alternativePerspectives.length} alternative perspectives.`);
215
+ // Step 8: Synthesize findings
216
+ progressStream.step(`Synthesizing final report...`, currentStep++);
217
+ await this.think("Synthesizing all findings into comprehensive critical analysis report...");
218
+ const synthesis = this.synthesizeChallenges(claims, challenges, alternativePerspectives, factCheckResults, docVerificationResults, multiModelConsensus);
219
+ await this.think("Critical analysis complete. Report generated with all verification results.");
220
+ // Complete progress
221
+ progressStream.complete(`Critical analysis complete: ${claims.length} claims, ${challenges.length} challenges`);
222
+ return {
223
+ claims,
224
+ counterArguments: challenges,
225
+ challenges, // Alias for backwards compatibility
226
+ uncontestedTone: { detected: groupthinkDetected, phrases: [], severity: 'low', message: '' },
227
+ thirdWay: [], // TODO: implement third-way logic
228
+ oppositeViews: [], // TODO: implement opposite views logic
229
+ groupthinkDetected,
230
+ alternativePerspectives,
231
+ factCheckResults,
232
+ docVerificationResults,
233
+ multiModelConsensus,
234
+ thinkingProcess: this.thinkingProcess,
235
+ synthesis
236
+ };
237
+ }
238
+ async think(thought) {
239
+ this.thinkingProcess.push(thought);
240
+ // Could optionally log to console if DEBUG=true
241
+ if (process.env.DEBUG === 'true') {
242
+ console.error(`[CHALLENGER THINK] ${thought}`);
243
+ }
244
+ }
245
+ async extractClaims(context, maxTokens) {
246
+ const text = this.contextToText(context);
247
+ const claims = [];
248
+ const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);
249
+ for (let i = 0; i < sentences.length && claims.length < 10; i++) {
250
+ const sentence = sentences[i].trim();
251
+ const claim = this.analyzeSentence(sentence, i);
252
+ if (claim) {
253
+ claims.push(claim);
254
+ }
255
+ }
256
+ return claims;
257
+ }
258
+ analyzeSentence(sentence, index) {
259
+ const factIndicators = ['is', 'are', 'was', 'were', 'has', 'have', 'will'];
260
+ const opinionIndicators = ['believe', 'think', 'feel', 'seems', 'appears', 'might'];
261
+ const assumptionIndicators = ['assume', 'presumably', 'probably', 'likely'];
262
+ const conclusionIndicators = ['therefore', 'thus', 'hence', 'so', 'conclusion'];
263
+ let type = 'fact';
264
+ let confidence = 0.5;
265
+ const lowerSentence = sentence.toLowerCase();
266
+ if (opinionIndicators.some(ind => lowerSentence.includes(ind))) {
267
+ type = 'opinion';
268
+ confidence = 0.3;
269
+ }
270
+ else if (assumptionIndicators.some(ind => lowerSentence.includes(ind))) {
271
+ type = 'assumption';
272
+ confidence = 0.4;
273
+ }
274
+ else if (conclusionIndicators.some(ind => lowerSentence.includes(ind))) {
275
+ type = 'conclusion';
276
+ confidence = 0.6;
277
+ }
278
+ else if (factIndicators.some(ind => lowerSentence.includes(ind))) {
279
+ type = 'fact';
280
+ confidence = 0.7;
281
+ }
282
+ if (sentence.length < 20) {
283
+ return null;
284
+ }
285
+ return {
286
+ id: `claim-${index}`,
287
+ text: sentence,
288
+ confidence,
289
+ type
290
+ };
291
+ }
292
+ async generateChallenges(claims, model, maxTokens, temperature) {
293
+ const challenges = [];
294
+ for (const claim of claims) {
295
+ const prompt = this.buildChallengePrompt(claim);
296
+ const response = await this.queryModel(model, prompt, maxTokens, temperature);
297
+ const challenge = this.parseChallenge(response, claim.id);
298
+ if (challenge) {
299
+ challenges.push(challenge);
300
+ }
301
+ }
302
+ return challenges;
303
+ }
304
+ buildChallengePrompt(claim) {
305
+ return `You are a critical analyst. Challenge this ${claim.type}:
306
+
307
+ "${claim.text}"
308
+
309
+ Respond in this exact format (replace everything after the colon with your actual analysis):
310
+
311
+ COUNTER-ARGUMENT: Your specific counter-argument goes here
312
+ ALTERNATIVE VIEW: Your alternative interpretation goes here
313
+ EVIDENCE NEEDED: What evidence would verify or refute this
314
+
315
+ Write concrete, specific analysis. Do NOT include brackets or placeholders.`;
316
+ }
317
+ async queryModel(model, prompt, maxTokens, temperature) {
318
+ try {
319
+ const { modelRouter } = await import('../utils/model-router.js');
320
+ const response = await modelRouter.callModel({
321
+ model,
322
+ prompt,
323
+ maxTokens,
324
+ temperature
325
+ });
326
+ return response.content;
327
+ }
328
+ catch (error) {
329
+ console.error(`Error querying ${model} for challenge:`, error);
330
+ throw new Error(`Failed to generate challenge: ${error.message}`);
331
+ }
332
+ }
333
+ parseChallenge(response, claimId) {
334
+ const lines = response.split('\n').filter(l => l.trim());
335
+ const challenge = lines.find(l => l.toLowerCase().includes('counter-argument'))?.split(':')[1]?.trim() ||
336
+ lines[0] ||
337
+ 'This claim requires further examination';
338
+ const alternativeView = lines.find(l => l.toLowerCase().includes('alternative'))?.split(':')[1]?.trim();
339
+ const evidence = lines.find(l => l.toLowerCase().includes('evidence'))?.split(':')[1]?.trim();
340
+ const severity = this.assessChallengeSeverity(challenge);
341
+ return {
342
+ claimId,
343
+ argument: challenge,
344
+ challenge,
345
+ evidence,
346
+ severity,
347
+ alternativeView
348
+ };
349
+ }
350
+ assessChallengeSeverity(challenge) {
351
+ const highSeverityWords = ['false', 'incorrect', 'wrong', 'dangerous', 'misleading'];
352
+ const mediumSeverityWords = ['questionable', 'uncertain', 'unclear', 'debatable'];
353
+ const lowerChallenge = challenge.toLowerCase();
354
+ if (highSeverityWords.some(word => lowerChallenge.includes(word))) {
355
+ return 'high';
356
+ }
357
+ if (mediumSeverityWords.some(word => lowerChallenge.includes(word))) {
358
+ return 'medium';
359
+ }
360
+ return 'low';
361
+ }
362
+ detectGroupthink(context) {
363
+ if (Array.isArray(context)) {
364
+ const responses = context.map(c => this.contextToText(c));
365
+ const uniqueViews = new Set(responses.map(r => this.extractKeyView(r)));
366
+ const consensusRatio = 1 - (uniqueViews.size / responses.length);
367
+ return consensusRatio > 0.8;
368
+ }
369
+ const text = this.contextToText(context);
370
+ const agreementPhrases = [
371
+ 'all agree', 'consensus is', 'everyone thinks',
372
+ 'unanimous', 'no disagreement', 'clearly'
373
+ ];
374
+ return agreementPhrases.some(phrase => text.toLowerCase().includes(phrase));
375
+ }
376
+ async generateAlternatives(claims, challenges, model) {
377
+ const alternatives = [];
378
+ const mainClaims = claims.filter(c => c.type === 'conclusion' || c.confidence > 0.6);
379
+ for (const claim of mainClaims.slice(0, 3)) {
380
+ const challenge = challenges.find(ch => ch.claimId === claim.id);
381
+ if (challenge?.alternativeView) {
382
+ alternatives.push(challenge.alternativeView);
383
+ }
384
+ else {
385
+ alternatives.push(`Alternative to "${claim.text.substring(0, 50)}...": Consider the opposite perspective`);
386
+ }
387
+ }
388
+ if (alternatives.length === 0) {
389
+ alternatives.push('Consider approaching this problem from a different angle');
390
+ alternatives.push('What if the fundamental assumptions are incorrect?');
391
+ alternatives.push('There may be unconsidered factors at play');
392
+ }
393
+ return alternatives;
394
+ }
395
+ async verifyFactsWithPerplexity(claims, thoroughness) {
396
+ const results = [];
397
+ const factClaims = claims.filter(c => c.type === 'fact' || c.type === 'conclusion');
398
+ const limit = thoroughness === 'deep' ? factClaims.length : Math.min(factClaims.length, 5);
399
+ // Get current date for temporal context
400
+ const currentDate = getCurrentDate();
401
+ for (const claim of factClaims.slice(0, limit)) {
402
+ try {
403
+ // Add date context to query
404
+ const dateAwareQuery = `[Current date: ${currentDate}] Verify this claim: "${claim.text}". Is it factually accurate? Provide evidence.`;
405
+ console.log(`[Challenger] Fact-checking with date context: ${currentDate}`);
406
+ // Use SmartAPIClient with retry logic
407
+ const timeoutConfig = getSmartTimeout(PROVIDER_PERPLEXITY, 'batch');
408
+ const response = await smartAPIClient.callWithRetries(() => this.queryModel('sonar-pro', dateAwareQuery, 1000, 0.3), {
409
+ provider: PROVIDER_PERPLEXITY,
410
+ priority: 'batch',
411
+ baseTimeoutMs: timeoutConfig.base,
412
+ maxTimeoutMs: timeoutConfig.max,
413
+ maxRetries: timeoutConfig.retries
414
+ });
415
+ // Parse response for verification
416
+ const verified = this.parseVerificationResult(response);
417
+ const confidence = this.parseConfidenceScore(response);
418
+ results.push({
419
+ claim: claim.text,
420
+ verified,
421
+ confidence,
422
+ findings: response
423
+ });
424
+ }
425
+ catch (error) {
426
+ console.error(`[Challenger] Error fact-checking claim with Perplexity:`, error);
427
+ results.push({
428
+ claim: claim.text,
429
+ verified: false,
430
+ confidence: 0,
431
+ findings: `Verification failed: ${error.message}`
432
+ });
433
+ }
434
+ }
435
+ return results;
436
+ }
437
+ async verifyDocsWithGrok(claims) {
438
+ const results = [];
439
+ const technicalClaims = claims.filter(c => this.isTechnicalClaim(c.text));
440
+ for (const claim of technicalClaims) {
441
+ try {
442
+ // Smart domain detection
443
+ const domains = this.detectDomainsFromClaim(claim.text);
444
+ // Skip if no programming-specific domains were found
445
+ // This prevents false positives where generic words triggered technical detection
446
+ if (domains.length === 0) {
447
+ console.log(`Skipping doc verification for claim (no programming domains found): ${claim.text.substring(0, 50)}...`);
448
+ continue;
449
+ }
450
+ // Use Grok search with domain restrictions
451
+ const query = `Find official documentation for: "${claim.text}"`;
452
+ const response = await this.queryGrokWithDomains(query, domains);
453
+ results.push({
454
+ claim: claim.text,
455
+ foundInDocs: response.found,
456
+ docsFound: response.found,
457
+ docReferences: domains,
458
+ officialSources: domains,
459
+ summary: response.summary,
460
+ confidence: response.found ? 0.8 : 0.3
461
+ });
462
+ }
463
+ catch (error) {
464
+ console.error(`Error verifying docs with Grok:`, error);
465
+ results.push({
466
+ claim: claim.text,
467
+ foundInDocs: false,
468
+ docsFound: false,
469
+ docReferences: [],
470
+ officialSources: [],
471
+ summary: `Doc verification failed: ${error.message}`,
472
+ confidence: 0
473
+ });
474
+ }
475
+ }
476
+ return results;
477
+ }
478
+ async getMultiModelConsensus(challenges, claims) {
479
+ try {
480
+ // Use multiple models for consensus - can't use verifier tool directly
481
+ // So we'll query 3 different models and compare their responses
482
+ const challengeTexts = challenges.map((ch, i) => {
483
+ const claim = claims.find(c => c.id === ch.claimId);
484
+ return `Challenge ${i + 1}: ${ch.challenge}\nOriginal Claim: ${claim?.text}`;
485
+ }).join('\n\n');
486
+ const query = `Verify these critical challenges and provide your assessment:\n\n${challengeTexts}`;
487
+ // Query models from configuration
488
+ const models = getChallengerModels();
489
+ const responses = await Promise.allSettled(models.map(model => this.queryModel(model, query, 2000, 0.5)));
490
+ // Collect successful responses
491
+ const successfulResponses = responses
492
+ .filter((r) => r.status === 'fulfilled')
493
+ .map(r => r.value);
494
+ if (successfulResponses.length === 0) {
495
+ throw new Error('All consensus queries failed');
496
+ }
497
+ // Combine responses
498
+ const combined = successfulResponses.join('\n\n---\n\n');
499
+ const agreement = this.parseAgreementLevel(combined);
500
+ return {
501
+ consensus: combined,
502
+ modelsUsed: models.slice(0, successfulResponses.length),
503
+ agreement
504
+ };
505
+ }
506
+ catch (error) {
507
+ console.error(`Error getting multi-model consensus:`, error);
508
+ return {
509
+ consensus: 'Consensus verification unavailable',
510
+ modelsUsed: [],
511
+ agreement: 0
512
+ };
513
+ }
514
+ }
515
+ detectDomainsFromClaim(text) {
516
+ const domains = [];
517
+ const lowerText = text.toLowerCase();
518
+ // Programming languages
519
+ if (lowerText.includes('python'))
520
+ domains.push('python.org', 'docs.python.org');
521
+ if (lowerText.includes('javascript') || lowerText.includes('js'))
522
+ domains.push('developer.mozilla.org', 'javascript.info');
523
+ if (lowerText.includes('typescript') || lowerText.includes('ts'))
524
+ domains.push('typescriptlang.org');
525
+ if (lowerText.includes('react'))
526
+ domains.push('react.dev', 'reactjs.org');
527
+ if (lowerText.includes('vue'))
528
+ domains.push('vuejs.org');
529
+ if (lowerText.includes('angular'))
530
+ domains.push('angular.io');
531
+ if (lowerText.includes('node'))
532
+ domains.push('nodejs.org');
533
+ if (lowerText.includes('rust'))
534
+ domains.push('rust-lang.org', 'doc.rust-lang.org');
535
+ if (lowerText.includes('go') || lowerText.includes('golang'))
536
+ domains.push('go.dev', 'golang.org');
537
+ if (lowerText.includes('java'))
538
+ domains.push('docs.oracle.com', 'openjdk.org');
539
+ // Frameworks & Libraries
540
+ if (lowerText.includes('django'))
541
+ domains.push('djangoproject.com');
542
+ if (lowerText.includes('flask'))
543
+ domains.push('flask.palletsprojects.com');
544
+ if (lowerText.includes('express'))
545
+ domains.push('expressjs.com');
546
+ if (lowerText.includes('nextjs') || lowerText.includes('next.js'))
547
+ domains.push('nextjs.org');
548
+ if (lowerText.includes('tailwind'))
549
+ domains.push('tailwindcss.com');
550
+ // Databases
551
+ if (lowerText.includes('postgres') || lowerText.includes('postgresql'))
552
+ domains.push('postgresql.org');
553
+ if (lowerText.includes('mysql'))
554
+ domains.push('dev.mysql.com');
555
+ if (lowerText.includes('mongodb'))
556
+ domains.push('mongodb.com');
557
+ if (lowerText.includes('redis'))
558
+ domains.push('redis.io');
559
+ // Cloud & DevOps
560
+ if (lowerText.includes('aws') || lowerText.includes('amazon web'))
561
+ domains.push('docs.aws.amazon.com');
562
+ if (lowerText.includes('azure'))
563
+ domains.push('docs.microsoft.com');
564
+ if (lowerText.includes('gcp') || lowerText.includes('google cloud'))
565
+ domains.push('cloud.google.com');
566
+ if (lowerText.includes('docker'))
567
+ domains.push('docs.docker.com');
568
+ if (lowerText.includes('kubernetes') || lowerText.includes('k8s'))
569
+ domains.push('kubernetes.io');
570
+ // AI & ML
571
+ if (lowerText.includes('tensorflow'))
572
+ domains.push('tensorflow.org');
573
+ if (lowerText.includes('pytorch'))
574
+ domains.push('pytorch.org');
575
+ if (lowerText.includes('openai'))
576
+ domains.push('platform.openai.com');
577
+ if (lowerText.includes('anthropic') || lowerText.includes('claude'))
578
+ domains.push('docs.anthropic.com');
579
+ // Don't add fallback domains - if no programming-specific domains were detected,
580
+ // this claim likely isn't technical and shouldn't trigger documentation verification
581
+ // Empty array will cause verifyDocsWithGrok to skip this claim appropriately
582
+ return domains;
583
+ }
584
+ isTechnicalClaim(text) {
585
+ // Only consider claims technical if they contain programming-specific keywords
586
+ // Removed generic words like 'system', 'performance', 'optimization' that appear in non-tech contexts
587
+ const technicalKeywords = [
588
+ 'api', 'code', 'function', 'class', 'method', 'library', 'framework',
589
+ 'database', 'server', 'client', 'algorithm', 'data structure',
590
+ 'programming', 'software', 'application', 'authentication', 'authorization',
591
+ // Programming languages
592
+ 'javascript', 'python', 'typescript', 'java', 'rust', 'golang', 'cpp',
593
+ // Tech-specific terms
594
+ 'endpoint', 'middleware', 'compiler', 'runtime', 'repository', 'git',
595
+ 'docker', 'kubernetes', 'deployment', 'ci/cd', 'testing framework'
596
+ ];
597
+ const lowerText = text.toLowerCase();
598
+ return technicalKeywords.some(keyword => lowerText.includes(keyword));
599
+ }
600
+ async queryGrokWithDomains(query, domains) {
601
+ try {
602
+ // Import callGrokEnhanced directly to use searchDomains parameter
603
+ const { callGrokEnhanced, GrokModel } = await import('../tools/grok-enhanced.js');
604
+ const messages = [
605
+ {
606
+ role: 'system',
607
+ content: 'You are Grok-3 with live search. Find official documentation and verify technical claims.'
608
+ },
609
+ {
610
+ role: 'user',
611
+ content: query
612
+ }
613
+ ];
614
+ // Use SmartAPIClient with retry logic
615
+ const timeoutConfig = getSmartTimeout(PROVIDER_GROK, 'batch');
616
+ const response = await smartAPIClient.callWithRetries(() => callGrokEnhanced(messages, {
617
+ model: GrokModel.GROK_3,
618
+ enableLiveSearch: true,
619
+ searchSources: 20,
620
+ searchDomains: domains.length > 0 ? domains : undefined,
621
+ temperature: 0.3,
622
+ maxTokens: 2000
623
+ }), {
624
+ provider: PROVIDER_GROK,
625
+ priority: 'batch',
626
+ baseTimeoutMs: timeoutConfig.base,
627
+ maxTimeoutMs: timeoutConfig.max,
628
+ maxRetries: timeoutConfig.retries
629
+ });
630
+ return {
631
+ found: response.content.length > 100,
632
+ summary: response.content
633
+ };
634
+ }
635
+ catch (error) {
636
+ console.error(`[Challenger] Error querying Grok with domains:`, error);
637
+ return {
638
+ found: false,
639
+ summary: `Search failed: ${error.message}`
640
+ };
641
+ }
642
+ }
643
+ parseVerificationResult(response) {
644
+ const lowerResponse = response.toLowerCase();
645
+ const positiveIndicators = ['verified', 'accurate', 'correct', 'true', 'confirmed', 'supported'];
646
+ const negativeIndicators = ['false', 'incorrect', 'inaccurate', 'disputed', 'unverified', 'misleading'];
647
+ const positiveCount = positiveIndicators.filter(ind => lowerResponse.includes(ind)).length;
648
+ const negativeCount = negativeIndicators.filter(ind => lowerResponse.includes(ind)).length;
649
+ return positiveCount > negativeCount;
650
+ }
651
+ parseConfidenceScore(response) {
652
+ // Look for explicit confidence mentions
653
+ const confidenceMatch = response.match(/(\d+)%?\s*confidence/i);
654
+ if (confidenceMatch) {
655
+ return parseInt(confidenceMatch[1]) / 100;
656
+ }
657
+ // Parse from language cues
658
+ const lowerResponse = response.toLowerCase();
659
+ if (lowerResponse.includes('definitely') || lowerResponse.includes('certainly'))
660
+ return 0.9;
661
+ if (lowerResponse.includes('likely') || lowerResponse.includes('probably'))
662
+ return 0.7;
663
+ if (lowerResponse.includes('possibly') || lowerResponse.includes('might'))
664
+ return 0.5;
665
+ if (lowerResponse.includes('unlikely') || lowerResponse.includes('doubtful'))
666
+ return 0.3;
667
+ return 0.5; // Default neutral
668
+ }
669
+ parseAgreementLevel(response) {
670
+ const lowerResponse = response.toLowerCase();
671
+ if (lowerResponse.includes('unanimous') || lowerResponse.includes('all agree'))
672
+ return 1.0;
673
+ if (lowerResponse.includes('strong consensus') || lowerResponse.includes('majority agree'))
674
+ return 0.8;
675
+ if (lowerResponse.includes('mixed') || lowerResponse.includes('divided'))
676
+ return 0.5;
677
+ if (lowerResponse.includes('disagreement') || lowerResponse.includes('conflicting'))
678
+ return 0.3;
679
+ return 0.6; // Default moderate agreement
680
+ }
681
+ synthesizeChallenges(claims, challenges, alternatives, factCheckResults, docVerificationResults, multiModelConsensus) {
682
+ let synthesis = `## šŸ” Critical Analysis Report\n\n`;
683
+ // Show thinking process if available
684
+ if (this.thinkingProcess.length > 0) {
685
+ synthesis += `### 🧠 Reasoning Process\n\n`;
686
+ this.thinkingProcess.forEach((thought, i) => {
687
+ synthesis += `${i + 1}. ${thought}\n`;
688
+ });
689
+ synthesis += `\n---\n\n`;
690
+ }
691
+ // Show extracted claims FIRST
692
+ synthesis += `### šŸ“‹ Claims Identified\n\n`;
693
+ claims.forEach((claim, i) => {
694
+ const icon = claim.type === 'fact' ? 'šŸ“Š' : claim.type === 'opinion' ? 'šŸ’­' : claim.type === 'assumption' ? 'šŸ¤”' : 'šŸ“Œ';
695
+ synthesis += `${icon} **Claim ${i + 1}** [${claim.type.toUpperCase()}] (${Math.round(claim.confidence * 100)}% confidence)\n`;
696
+ synthesis += `> "${claim.text}"\n\n`;
697
+ });
698
+ // Show actual challenges with full context
699
+ if (challenges.length > 0) {
700
+ synthesis += `### āš ļø Counter-Arguments Generated\n\n`;
701
+ challenges.forEach((ch, i) => {
702
+ const claim = claims.find(c => c.id === ch.claimId);
703
+ const severityIcon = ch.severity === 'high' ? 'šŸ”“' : ch.severity === 'medium' ? '🟔' : '🟢';
704
+ synthesis += `${severityIcon} **Challenge #${i + 1}** (${ch.severity} severity)\n`;
705
+ synthesis += `**Original Claim:** "${claim?.text}"\n\n`;
706
+ synthesis += `**Counter-Argument:**\n${ch.challenge}\n\n`;
707
+ if (ch.alternativeView) {
708
+ synthesis += `**Alternative View:** ${ch.alternativeView}\n\n`;
709
+ }
710
+ if (ch.evidence) {
711
+ synthesis += `**Evidence Needed:** ${ch.evidence}\n\n`;
712
+ }
713
+ synthesis += `---\n\n`;
714
+ });
715
+ }
716
+ // Show fact-check results from Perplexity
717
+ if (factCheckResults && factCheckResults.length > 0) {
718
+ synthesis += `### āœ… Fact Verification (Perplexity)\n\n`;
719
+ const verified = factCheckResults.filter(r => r.verified).length;
720
+ const disputed = factCheckResults.filter(r => !r.verified).length;
721
+ synthesis += `**Summary:** ${verified} verified, ${disputed} disputed (out of ${factCheckResults.length} checked)\n\n`;
722
+ factCheckResults.forEach((result, i) => {
723
+ const icon = result.verified ? 'āœ…' : 'āŒ';
724
+ const confidencePercent = Math.round(result.confidence * 100);
725
+ synthesis += `${icon} **Fact Check #${i + 1}** (${confidencePercent}% confidence)\n`;
726
+ synthesis += `**Claim:** "${result.claim.substring(0, 100)}${result.claim.length > 100 ? '...' : ''}"\n`;
727
+ synthesis += `**Status:** ${result.verified ? 'VERIFIED' : 'DISPUTED'}\n`;
728
+ if (result.findings) {
729
+ synthesis += `**Findings:** ${result.findings.substring(0, 300)}...\n`;
730
+ }
731
+ synthesis += `\n`;
732
+ });
733
+ synthesis += `---\n\n`;
734
+ }
735
+ // Show documentation verification from Grok
736
+ if (docVerificationResults && docVerificationResults.length > 0) {
737
+ synthesis += `### šŸ“š Documentation Verification (Grok)\n\n`;
738
+ const docsFound = docVerificationResults.filter(r => r.docsFound).length;
739
+ synthesis += `**Summary:** ${docsFound} out of ${docVerificationResults.length} technical claims have supporting documentation\n\n`;
740
+ docVerificationResults.forEach((result, i) => {
741
+ const icon = result.docsFound ? 'šŸ“–' : 'ā“';
742
+ synthesis += `${icon} **Doc Check #${i + 1}**\n`;
743
+ synthesis += `**Claim:** "${result.claim.substring(0, 100)}${result.claim.length > 100 ? '...' : ''}"\n`;
744
+ synthesis += `**Official Sources Checked:** ${result.officialSources?.join(', ') || 'None'}\n`;
745
+ synthesis += `**Status:** ${result.docsFound ? 'Documentation found' : 'No official docs found'}\n`;
746
+ if (result.summary) {
747
+ synthesis += `**Summary:** ${result.summary.substring(0, 250)}...\n`;
748
+ }
749
+ synthesis += `\n`;
750
+ });
751
+ synthesis += `---\n\n`;
752
+ }
753
+ // Show multi-model consensus
754
+ if (multiModelConsensus && multiModelConsensus.consensus) {
755
+ synthesis += `### šŸ¤– Multi-Model Consensus\n\n`;
756
+ const agreementPercent = Math.round(multiModelConsensus.agreement * 100);
757
+ synthesis += `**Models Used:** ${multiModelConsensus.modelsUsed.join(', ')}\n`;
758
+ synthesis += `**Agreement Level:** ${agreementPercent}%\n\n`;
759
+ synthesis += `**Consensus Analysis:**\n${multiModelConsensus.consensus}\n\n`;
760
+ synthesis += `---\n\n`;
761
+ }
762
+ // Explain groupthink in human terms
763
+ const groupthinkRisk = this.detectGroupthink(claims) ? 'HIGH' : 'LOW';
764
+ synthesis += `### šŸŽ­ Echo Chamber Detection\n\n`;
765
+ if (groupthinkRisk === 'HIGH') {
766
+ synthesis += `āš ļø **HIGH GROUPTHINK RISK DETECTED**\n\n`;
767
+ synthesis += `**What this means:** The input shows signs of "echo chamber" thinking - where everyone agrees without challenging assumptions. This is risky because:\n`;
768
+ synthesis += `- No diverse perspectives are considered\n`;
769
+ synthesis += `- Potential flaws go unexamined\n`;
770
+ synthesis += `- Confirmation bias may be at play\n\n`;
771
+ synthesis += `**Alternative perspectives to consider:**\n`;
772
+ alternatives.slice(0, 3).forEach(alt => synthesis += `- ${alt}\n`);
773
+ // Visual indicator
774
+ synthesis += `\nšŸ“Š **Diversity Score:**\n`;
775
+ synthesis += `\`\`\`\n`;
776
+ synthesis += `[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘] 80%+ agreement → Echo chamber likely\n`;
777
+ synthesis += `\`\`\`\n`;
778
+ }
779
+ else {
780
+ synthesis += `āœ… **LOW GROUPTHINK RISK**\n\n`;
781
+ synthesis += `**What this means:** The input shows healthy diversity of thought. Different perspectives are present, reducing the risk of echo chamber effects.\n\n`;
782
+ synthesis += `šŸ“Š **Diversity Score:**\n`;
783
+ synthesis += `\`\`\`\n`;
784
+ synthesis += `[ā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] <80% agreement → Healthy diversity\n`;
785
+ synthesis += `\`\`\`\n`;
786
+ }
787
+ synthesis += `\n`;
788
+ // Visual summary with stats
789
+ const factClaims = claims.filter(c => c.type === 'fact');
790
+ const opinionClaims = claims.filter(c => c.type === 'opinion');
791
+ const assumptionClaims = claims.filter(c => c.type === 'assumption');
792
+ const conclusionClaims = claims.filter(c => c.type === 'conclusion');
793
+ const highSeverity = challenges.filter(c => c.severity === 'high').length;
794
+ const mediumSeverity = challenges.filter(c => c.severity === 'medium').length;
795
+ const lowSeverity = challenges.filter(c => c.severity === 'low').length;
796
+ synthesis += `### šŸ“Š Analysis Summary\n\n`;
797
+ synthesis += `**Claims Breakdown:**\n`;
798
+ synthesis += `\`\`\`\n`;
799
+ synthesis += `Total Claims: ${claims.length}\n`;
800
+ if (factClaims.length > 0)
801
+ synthesis += ` ā”œā”€ šŸ“Š Facts: ${factClaims.length}\n`;
802
+ if (opinionClaims.length > 0)
803
+ synthesis += ` ā”œā”€ šŸ’­ Opinions: ${opinionClaims.length}\n`;
804
+ if (assumptionClaims.length > 0)
805
+ synthesis += ` ā”œā”€ šŸ¤” Assumptions: ${assumptionClaims.length}\n`;
806
+ if (conclusionClaims.length > 0)
807
+ synthesis += ` └─ šŸ“Œ Conclusions: ${conclusionClaims.length}\n`;
808
+ synthesis += `\`\`\`\n\n`;
809
+ synthesis += `**Challenges Generated:**\n`;
810
+ synthesis += `\`\`\`\n`;
811
+ synthesis += `Total Challenges: ${challenges.length}\n`;
812
+ if (highSeverity > 0)
813
+ synthesis += ` ā”œā”€ šŸ”“ High Severity: ${highSeverity}\n`;
814
+ if (mediumSeverity > 0)
815
+ synthesis += ` ā”œā”€ 🟔 Medium Severity: ${mediumSeverity}\n`;
816
+ if (lowSeverity > 0)
817
+ synthesis += ` └─ 🟢 Low Severity: ${lowSeverity}\n`;
818
+ synthesis += `\`\`\`\n\n`;
819
+ synthesis += `**Alternative Perspectives:** ${alternatives.length} generated\n`;
820
+ synthesis += `**Groupthink Risk:** ${groupthinkRisk}\n`;
821
+ return synthesis;
822
+ }
823
+ contextToText(context) {
824
+ if (typeof context === 'string')
825
+ return context;
826
+ if (typeof context === 'object' && context.query)
827
+ return context.query;
828
+ if (typeof context === 'object' && context.text)
829
+ return context.text;
830
+ if (typeof context === 'object' && context.content)
831
+ return context.content;
832
+ return JSON.stringify(context);
833
+ }
834
+ extractKeyView(text) {
835
+ const sentences = text.split(/[.!?]+/);
836
+ const conclusionSentence = sentences.find(s => s.toLowerCase().includes('conclusion') ||
837
+ s.toLowerCase().includes('therefore') ||
838
+ s.toLowerCase().includes('believe'));
839
+ return conclusionSentence || sentences[0] || text.substring(0, 100);
840
+ }
841
+ }