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.
- package/.env.example +260 -0
- package/CHANGELOG.md +54 -0
- package/CODE_OF_CONDUCT.md +56 -0
- package/CONTRIBUTING.md +54 -0
- package/Dockerfile +36 -0
- package/LICENSE +644 -0
- package/README.md +201 -0
- package/SECURITY.md +95 -0
- package/dist/personality/komaai-expressions.js +12 -0
- package/dist/profiles/balanced.json +33 -0
- package/dist/profiles/code_focus.json +33 -0
- package/dist/profiles/full.json +33 -0
- package/dist/profiles/minimal.json +33 -0
- package/dist/profiles/research_power.json +33 -0
- package/dist/scripts/build-profiles.js +46 -0
- package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
- package/dist/src/application/services/focus/FocusTool.service.js +109 -0
- package/dist/src/application/services/focus/ModeRegistry.js +46 -0
- package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
- package/dist/src/application/services/focus/modes/status.mode.js +50 -0
- package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
- package/dist/src/collaborative-orchestrator.js +391 -0
- package/dist/src/config/model-constants.js +188 -0
- package/dist/src/config/model-defaults.js +57 -0
- package/dist/src/config/model-preferences.js +382 -0
- package/dist/src/config/timeout-config.js +130 -0
- package/dist/src/config.js +173 -0
- package/dist/src/domain/interfaces/IFocusMode.js +5 -0
- package/dist/src/domain/interfaces/IProvider.js +6 -0
- package/dist/src/domain/interfaces/ITool.js +5 -0
- package/dist/src/focus-deep.js +245 -0
- package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
- package/dist/src/mcp-client.js +90 -0
- package/dist/src/memory/index.js +17 -0
- package/dist/src/memory/memory-config.js +135 -0
- package/dist/src/memory/memory-interface.js +174 -0
- package/dist/src/memory/memory-manager.js +383 -0
- package/dist/src/memory/providers/devlog-provider.js +385 -0
- package/dist/src/memory/providers/hybrid-provider.js +399 -0
- package/dist/src/memory/providers/local-provider.js +388 -0
- package/dist/src/memory/providers/mem0-provider.js +337 -0
- package/dist/src/modes/architect.js +477 -0
- package/dist/src/modes/auditor.js +362 -0
- package/dist/src/modes/challenger.js +841 -0
- package/dist/src/modes/code-reviewer.js +382 -0
- package/dist/src/modes/commit-guardian.js +424 -0
- package/dist/src/modes/documentation-writer.js +572 -0
- package/dist/src/modes/scout.js +587 -0
- package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
- package/dist/src/modes/shared/helpers/index.js +17 -0
- package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
- package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
- package/dist/src/modes/test-architect.js +767 -0
- package/dist/src/modes/verifier.js +378 -0
- package/dist/src/monitoring/performance-monitor.js +435 -0
- package/dist/src/optimization/batch-executor.js +121 -0
- package/dist/src/optimization/context-pruner.js +196 -0
- package/dist/src/optimization/cost-monitor.js +338 -0
- package/dist/src/optimization/index.js +65 -0
- package/dist/src/optimization/model-router.js +264 -0
- package/dist/src/optimization/result-cache.js +114 -0
- package/dist/src/optimization/token-optimizer.js +257 -0
- package/dist/src/optimization/token-tracker.js +118 -0
- package/dist/src/orchestrator-instructions.js +128 -0
- package/dist/src/orchestrator-lite.js +139 -0
- package/dist/src/orchestrator.js +191 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
- package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
- package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
- package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
- package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
- package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
- package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
- package/dist/src/profiles/balanced.js +37 -0
- package/dist/src/profiles/code_focus.js +37 -0
- package/dist/src/profiles/debug_intensive.js +59 -0
- package/dist/src/profiles/full.js +37 -0
- package/dist/src/profiles/minimal.js +37 -0
- package/dist/src/profiles/research_code.js +59 -0
- package/dist/src/profiles/research_power.js +37 -0
- package/dist/src/profiles/types.js +5 -0
- package/dist/src/profiles/workflow_builder.js +53 -0
- package/dist/src/prompt-engineer-lite.js +78 -0
- package/dist/src/prompt-engineer.js +399 -0
- package/dist/src/reasoning-chain.js +508 -0
- package/dist/src/sequential-thinking.js +291 -0
- package/dist/src/server-diagnostic.js +74 -0
- package/dist/src/server-raw.js +158 -0
- package/dist/src/server-simple.js +58 -0
- package/dist/src/server.js +514 -0
- package/dist/src/session/session-logger.js +617 -0
- package/dist/src/session/session-manager.js +571 -0
- package/dist/src/session/session-tools.js +400 -0
- package/dist/src/tools/advanced-modes.js +200 -0
- package/dist/src/tools/claude-integration.js +356 -0
- package/dist/src/tools/consolidated/ai-router.js +174 -0
- package/dist/src/tools/consolidated/ai-tool.js +48 -0
- package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
- package/dist/src/tools/consolidated/environment-detector.js +80 -0
- package/dist/src/tools/consolidated/index.js +50 -0
- package/dist/src/tools/consolidated/search-tool.js +110 -0
- package/dist/src/tools/consolidated/workflow-tool.js +238 -0
- package/dist/src/tools/gemini-tools.js +329 -0
- package/dist/src/tools/grok-enhanced.js +376 -0
- package/dist/src/tools/grok-tools.js +299 -0
- package/dist/src/tools/lmstudio-tools.js +223 -0
- package/dist/src/tools/openai-tools.js +498 -0
- package/dist/src/tools/openrouter-tools.js +317 -0
- package/dist/src/tools/optimized-wrapper.js +204 -0
- package/dist/src/tools/perplexity-tools.js +294 -0
- package/dist/src/tools/pingpong-tool.js +343 -0
- package/dist/src/tools/qwen-wrapper.js +74 -0
- package/dist/src/tools/tool-router.js +444 -0
- package/dist/src/tools/unified-ai-provider.js +260 -0
- package/dist/src/tools/workflow-runner.js +425 -0
- package/dist/src/tools/workflow-validator-tool.js +107 -0
- package/dist/src/types.js +23 -0
- package/dist/src/utils/input-validator.js +130 -0
- package/dist/src/utils/model-router.js +91 -0
- package/dist/src/utils/progress-stream.js +255 -0
- package/dist/src/utils/provider-router.js +88 -0
- package/dist/src/utils/smart-api-client.js +146 -0
- package/dist/src/utils/table-builder.js +218 -0
- package/dist/src/utils/timestamp-formatter.js +134 -0
- package/dist/src/utils/tool-compressor.js +257 -0
- package/dist/src/utils/tool-config.js +201 -0
- package/dist/src/validators/dependency-graph-validator.js +147 -0
- package/dist/src/validators/interpolation-validator.js +222 -0
- package/dist/src/validators/output-usage-validator.js +151 -0
- package/dist/src/validators/syntax-validator.js +102 -0
- package/dist/src/validators/tool-registry-validator.js +123 -0
- package/dist/src/validators/tool-types.js +97 -0
- package/dist/src/validators/types.js +8 -0
- package/dist/src/validators/workflow-validator.js +134 -0
- package/dist/src/visualizer-lite.js +42 -0
- package/dist/src/visualizer.js +179 -0
- package/dist/src/workflows/circuit-breaker.js +199 -0
- package/dist/src/workflows/custom-workflows.js +451 -0
- package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
- package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
- package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
- package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
- package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
- package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
- package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
- package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
- package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
- package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
- package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
- package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
- package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
- package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
- package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
- package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
- package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
- package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
- package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
- package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
- package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
- package/dist/src/workflows/fallback-strategies.js +373 -0
- package/dist/src/workflows/message-queue.js +455 -0
- package/dist/src/workflows/model-router.js +189 -0
- package/dist/src/workflows/orchestrator-examples.js +174 -0
- package/dist/src/workflows/orchestrator-integration.js +200 -0
- package/dist/src/workflows/self-healing.js +524 -0
- package/dist/src/workflows/tool-mapper.js +407 -0
- package/dist/src/workflows/tool-orchestrator.js +796 -0
- package/dist/src/workflows/workflow-engine.js +573 -0
- package/dist/src/workflows/workflow-parser.js +283 -0
- package/dist/src/workflows/workflow-types.js +95 -0
- package/dist/src/workflows.js +568 -0
- package/dist/test-workflow-file-output.js +93 -0
- package/docs/API_KEYS.md +570 -0
- package/docs/CLAUDE_CODE_SETUP.md +181 -0
- package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
- package/docs/CONFIGURATION.md +745 -0
- package/docs/FOCUS_MODES.md +240 -0
- package/docs/INSTALLATION_BOTH.md +145 -0
- package/docs/TERMS.md +352 -0
- package/docs/TOOLS_REFERENCE.md +1622 -0
- package/docs/TOOL_PARAMETERS.md +496 -0
- package/docs/TOOL_PROFILES.md +236 -0
- package/docs/WORKFLOWS.md +987 -0
- package/docs/WORKFLOW_OUTPUT.md +198 -0
- package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
- package/docs/workflows/design-brainstorm.md +335 -0
- package/package.json +97 -0
- package/profiles/balanced.json +37 -0
- package/profiles/code_focus.json +37 -0
- package/profiles/debug_intensive.json +34 -0
- package/profiles/full.json +37 -0
- package/profiles/minimal.json +37 -0
- package/profiles/research_power.json +37 -0
- package/profiles/workflow_builder.json +37 -0
- package/smithery.yaml +66 -0
- package/start.sh +8 -0
- package/tools.config.json +81 -0
- package/tsconfig.json +18 -0
- package/workflows/accessibility-code-audit.yaml +92 -0
- package/workflows/code-architecture-review.yaml +202 -0
- package/workflows/code-review.yaml +142 -0
- package/workflows/core/iterative-problem-solver.yaml +283 -0
- package/workflows/creative-brainstorm-yaml.yaml +215 -0
- package/workflows/pingpong.yaml +141 -0
- package/workflows/system/README.md +412 -0
- package/workflows/system/challenger.yaml +175 -0
- package/workflows/system/scout.yaml +164 -0
- package/workflows/system/verifier.yaml +133 -0
- package/workflows/ultra-creative-brainstorm.yaml +318 -0
- 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
|
+
}
|