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,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scout Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* Pure, composable functions extracted from Scout mode.
|
|
5
|
+
* Used by workflows/system/scout.yaml
|
|
6
|
+
*
|
|
7
|
+
* SOLID Principles:
|
|
8
|
+
* - Single Responsibility: Each function does ONE transformation
|
|
9
|
+
* - Open/Closed: Functions are closed for modification, open for extension
|
|
10
|
+
* - Liskov Substitution: All functions are predictable pure functions
|
|
11
|
+
* - Interface Segregation: Small, focused function signatures
|
|
12
|
+
* - Dependency Inversion: Functions depend on types, not concrete implementations
|
|
13
|
+
*/
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Query Preparation
|
|
16
|
+
// ============================================================================
|
|
17
|
+
/**
|
|
18
|
+
* Prepend current date to query for temporal context
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* prepareQuery("What's new in React?", "October 21, 2025")
|
|
22
|
+
* // => "[Current date: October 21, 2025] What's new in React?"
|
|
23
|
+
*/
|
|
24
|
+
export const prepareQuery = (query, date) => {
|
|
25
|
+
return `[Current date: ${date}] ${query}`;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Craft targeted probe based on query type detection
|
|
29
|
+
*
|
|
30
|
+
* Identifies question type (latest, technical, comparison) and
|
|
31
|
+
* reformulates query for better search results.
|
|
32
|
+
*/
|
|
33
|
+
export const craftTargetedProbe = (query) => {
|
|
34
|
+
const keywords = extractKeywords(query);
|
|
35
|
+
const questionType = identifyQuestionType(query);
|
|
36
|
+
switch (questionType) {
|
|
37
|
+
case 'latest':
|
|
38
|
+
return `Latest information about ${keywords.join(' ')} as of 2025`;
|
|
39
|
+
case 'technical':
|
|
40
|
+
return `Technical documentation and API details for ${keywords.join(' ')}`;
|
|
41
|
+
case 'comparison':
|
|
42
|
+
return `Compare ${keywords.join(' vs ')} with current data`;
|
|
43
|
+
default:
|
|
44
|
+
return query;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Fact Extraction & Validation
|
|
49
|
+
// ============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Extract structured facts from search result text
|
|
52
|
+
*
|
|
53
|
+
* Returns array of fact strings (lines > 20 chars, max 5 facts)
|
|
54
|
+
*/
|
|
55
|
+
export const extractFacts = (searchResult) => {
|
|
56
|
+
const lines = searchResult.split('\n');
|
|
57
|
+
const facts = lines
|
|
58
|
+
.filter(line => line.trim().length > 20)
|
|
59
|
+
.slice(0, 5)
|
|
60
|
+
.map(line => line.trim());
|
|
61
|
+
const sources = extractSources(searchResult);
|
|
62
|
+
const reliability = assessReliability(searchResult);
|
|
63
|
+
return {
|
|
64
|
+
facts,
|
|
65
|
+
sources,
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
reliability
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Validate fact quality and reliability
|
|
72
|
+
*
|
|
73
|
+
* Returns true if facts are sufficient for analysis
|
|
74
|
+
*/
|
|
75
|
+
export const validateFacts = (facts) => {
|
|
76
|
+
return facts.facts.length > 0 && facts.reliability > 0.5;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Select best facts from quick scan or deep search
|
|
80
|
+
*/
|
|
81
|
+
export const selectBestFacts = (quickFacts, deepFacts, valid) => {
|
|
82
|
+
if (valid && quickFacts) {
|
|
83
|
+
return quickFacts;
|
|
84
|
+
}
|
|
85
|
+
return deepFacts;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Verify fact reliability
|
|
89
|
+
*/
|
|
90
|
+
export const verifyFacts = (facts) => {
|
|
91
|
+
return {
|
|
92
|
+
verified: true,
|
|
93
|
+
confidence: facts.reliability
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Context Building
|
|
98
|
+
// ============================================================================
|
|
99
|
+
/**
|
|
100
|
+
* Build context string from facts for model analysis
|
|
101
|
+
*/
|
|
102
|
+
export const buildContext = (facts, timestamp) => {
|
|
103
|
+
const ts = timestamp || facts.timestamp;
|
|
104
|
+
const sourceList = facts.sources?.join(', ') || 'Not specified';
|
|
105
|
+
return `Context (${ts}):\n${facts.facts.join('\n')}\n\nSources: ${sourceList}`;
|
|
106
|
+
};
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Insight Extraction
|
|
109
|
+
// ============================================================================
|
|
110
|
+
/**
|
|
111
|
+
* Extract key insights from model analyses
|
|
112
|
+
*
|
|
113
|
+
* Returns array of AnalysisResult with extracted insights
|
|
114
|
+
*/
|
|
115
|
+
export const extractInsights = (analyses) => {
|
|
116
|
+
return analyses.map(({ model, content }) => ({
|
|
117
|
+
model,
|
|
118
|
+
analysis: content,
|
|
119
|
+
insights: extractInsightsFromText(content)
|
|
120
|
+
}));
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Extract insight bullet points from text
|
|
124
|
+
*/
|
|
125
|
+
const extractInsightsFromText = (text) => {
|
|
126
|
+
const lines = text.split('\n');
|
|
127
|
+
return lines
|
|
128
|
+
.filter(line => line.includes('insight') ||
|
|
129
|
+
line.includes('Key') ||
|
|
130
|
+
line.startsWith('•') ||
|
|
131
|
+
line.startsWith('-'))
|
|
132
|
+
.map(line => line.replace(/^[•\-]\s*/, '').trim())
|
|
133
|
+
.filter(line => line.length > 10)
|
|
134
|
+
.slice(0, 3);
|
|
135
|
+
};
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// Synthesis
|
|
138
|
+
// ============================================================================
|
|
139
|
+
/**
|
|
140
|
+
* Synthesize facts and analyses into readable report
|
|
141
|
+
*/
|
|
142
|
+
export const synthesizeScoutReport = (facts, insights) => {
|
|
143
|
+
let synthesis = '';
|
|
144
|
+
// Add facts section
|
|
145
|
+
if (facts && facts.facts.length > 0) {
|
|
146
|
+
synthesis += `Current Information (${facts.timestamp}):\n`;
|
|
147
|
+
facts.facts.slice(0, 3).forEach(fact => {
|
|
148
|
+
synthesis += `• ${fact}\n`;
|
|
149
|
+
});
|
|
150
|
+
synthesis += '\n';
|
|
151
|
+
}
|
|
152
|
+
// Add analysis section
|
|
153
|
+
synthesis += 'Analysis:\n';
|
|
154
|
+
insights.forEach(analysis => {
|
|
155
|
+
synthesis += `${analysis.model}:\n`;
|
|
156
|
+
analysis.insights.slice(0, 2).forEach(insight => {
|
|
157
|
+
synthesis += `• ${insight}\n`;
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
return synthesis;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Synthesize waterfall verification results
|
|
164
|
+
*/
|
|
165
|
+
export const synthesizeScoutWaterfall = (facts, verification) => {
|
|
166
|
+
return `Verified Information:\n${facts.facts.join('\n')}\n\nReliability: ${facts.reliability}`;
|
|
167
|
+
};
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Report Formatting
|
|
170
|
+
// ============================================================================
|
|
171
|
+
/**
|
|
172
|
+
* Format final Scout report with metadata
|
|
173
|
+
*/
|
|
174
|
+
export const formatScoutReport = (config) => {
|
|
175
|
+
return {
|
|
176
|
+
synthesis: config.synthesis,
|
|
177
|
+
facts: config.facts || undefined,
|
|
178
|
+
analyses_count: config.analyses_count,
|
|
179
|
+
execution_time: config.execution_time,
|
|
180
|
+
warning: config.warning || undefined,
|
|
181
|
+
verification: config.verification
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Internal Utilities (not exported, used by public functions)
|
|
186
|
+
// ============================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Extract keywords from query (remove stop words)
|
|
189
|
+
*/
|
|
190
|
+
const extractKeywords = (query) => {
|
|
191
|
+
const words = query.toLowerCase().split(/\s+/);
|
|
192
|
+
const stopWords = ['what', 'how', 'why', 'when', 'where', 'is', 'are', 'the', 'a', 'an'];
|
|
193
|
+
return words.filter(w => !stopWords.includes(w) && w.length > 2);
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Identify question type from query text
|
|
197
|
+
*/
|
|
198
|
+
const identifyQuestionType = (query) => {
|
|
199
|
+
const lower = query.toLowerCase();
|
|
200
|
+
if (lower.includes('latest') || lower.includes('current') || lower.includes('new')) {
|
|
201
|
+
return 'latest';
|
|
202
|
+
}
|
|
203
|
+
if (lower.includes('api') || lower.includes('function') || lower.includes('method')) {
|
|
204
|
+
return 'technical';
|
|
205
|
+
}
|
|
206
|
+
if (lower.includes('vs') || lower.includes('compare') || lower.includes('difference')) {
|
|
207
|
+
return 'comparison';
|
|
208
|
+
}
|
|
209
|
+
return 'general';
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Extract source citations from text
|
|
213
|
+
*/
|
|
214
|
+
const extractSources = (text) => {
|
|
215
|
+
const sourcePattern = /\[(\d+)\]\s*([^\n]+)/g;
|
|
216
|
+
const sources = [];
|
|
217
|
+
let match;
|
|
218
|
+
while ((match = sourcePattern.exec(text)) !== null) {
|
|
219
|
+
sources.push(match[2]);
|
|
220
|
+
}
|
|
221
|
+
return sources;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Assess reliability score based on content indicators
|
|
225
|
+
*
|
|
226
|
+
* Scoring:
|
|
227
|
+
* - Base: 0.5
|
|
228
|
+
* - Has sources/citations: +0.2
|
|
229
|
+
* - Has recent dates (2024/2025): +0.1
|
|
230
|
+
* - Has verification keywords: +0.1
|
|
231
|
+
* - Lengthy content (> 500 chars): +0.1
|
|
232
|
+
* - Max: 1.0
|
|
233
|
+
*/
|
|
234
|
+
const assessReliability = (text) => {
|
|
235
|
+
let score = 0.5;
|
|
236
|
+
if (text.includes('source') || text.includes('['))
|
|
237
|
+
score += 0.2;
|
|
238
|
+
if (text.includes('2025') || text.includes('2024'))
|
|
239
|
+
score += 0.1;
|
|
240
|
+
if (text.includes('verified') || text.includes('confirmed'))
|
|
241
|
+
score += 0.1;
|
|
242
|
+
if (text.length > 500)
|
|
243
|
+
score += 0.1;
|
|
244
|
+
return Math.min(score, 1.0);
|
|
245
|
+
};
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Type Guards
|
|
248
|
+
// ============================================================================
|
|
249
|
+
/**
|
|
250
|
+
* Check if value is a valid FactResult
|
|
251
|
+
*/
|
|
252
|
+
export const isFactResult = (value) => {
|
|
253
|
+
if (!value || typeof value !== 'object')
|
|
254
|
+
return false;
|
|
255
|
+
const obj = value;
|
|
256
|
+
return (Array.isArray(obj.facts) &&
|
|
257
|
+
typeof obj.timestamp === 'string' &&
|
|
258
|
+
typeof obj.reliability === 'number');
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* Check if value is a valid AnalysisResult
|
|
262
|
+
*/
|
|
263
|
+
export const isAnalysisResult = (value) => {
|
|
264
|
+
if (!value || typeof value !== 'object')
|
|
265
|
+
return false;
|
|
266
|
+
const obj = value;
|
|
267
|
+
return (typeof obj.model === 'string' &&
|
|
268
|
+
typeof obj.analysis === 'string' &&
|
|
269
|
+
Array.isArray(obj.insights));
|
|
270
|
+
};
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifier Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* Pure, composable functions for multi-model verification and consensus analysis.
|
|
5
|
+
* Used by workflows/system/verifier.yaml
|
|
6
|
+
*
|
|
7
|
+
* SOLID Principles:
|
|
8
|
+
* - Single Responsibility: Each function does ONE transformation
|
|
9
|
+
* - Open/Closed: Functions are closed for modification, open for extension
|
|
10
|
+
* - Liskov Substitution: All functions are predictable pure functions
|
|
11
|
+
* - Interface Segregation: Small, focused function signatures
|
|
12
|
+
* - Dependency Inversion: Functions depend on types, not concrete implementations
|
|
13
|
+
*/
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Prompt Building
|
|
16
|
+
// ============================================================================
|
|
17
|
+
/**
|
|
18
|
+
* Build verification prompt with optional source requirements
|
|
19
|
+
*/
|
|
20
|
+
export const buildPrompt = (config) => {
|
|
21
|
+
const basePrompt = `Analyze the following query/statement critically and provide your assessment.
|
|
22
|
+
|
|
23
|
+
Query: ${config.query}
|
|
24
|
+
|
|
25
|
+
Please provide:
|
|
26
|
+
1. Your conclusion (true/false/uncertain/needs-context)
|
|
27
|
+
2. Key reasoning points
|
|
28
|
+
3. Confidence level (0-100%)`;
|
|
29
|
+
if (config.include_sources) {
|
|
30
|
+
return basePrompt + `
|
|
31
|
+
4. Supporting evidence or sources
|
|
32
|
+
5. Any contradicting information found`;
|
|
33
|
+
}
|
|
34
|
+
return basePrompt;
|
|
35
|
+
};
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Response Extraction
|
|
38
|
+
// ============================================================================
|
|
39
|
+
/**
|
|
40
|
+
* Extract structured data from raw model responses
|
|
41
|
+
*/
|
|
42
|
+
export const extractModelResponses = (config) => {
|
|
43
|
+
return config.raw_responses.map((response, index) => ({
|
|
44
|
+
model: config.models[index] || response.model,
|
|
45
|
+
response: response.content,
|
|
46
|
+
conclusion: extractConclusion(response.content),
|
|
47
|
+
evidence: config.include_sources ? extractEvidence(response.content) : undefined,
|
|
48
|
+
confidence: extractConfidence(response.content),
|
|
49
|
+
tokens: estimateTokens(response.content)
|
|
50
|
+
}));
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Extract conclusion from response text
|
|
54
|
+
*
|
|
55
|
+
* Patterns checked (in order):
|
|
56
|
+
* 1. "conclusion: ..." / "answer: ..." / "verdict: ..." / "result: ..."
|
|
57
|
+
* 2. Keywords: "true", "false", "uncertain"
|
|
58
|
+
* 3. Default: "unknown"
|
|
59
|
+
*/
|
|
60
|
+
export const extractConclusion = (content) => {
|
|
61
|
+
const patterns = [
|
|
62
|
+
/conclusion:\s*([^\n]+)/i,
|
|
63
|
+
/answer:\s*([^\n]+)/i,
|
|
64
|
+
/verdict:\s*([^\n]+)/i,
|
|
65
|
+
/result:\s*([^\n]+)/i
|
|
66
|
+
];
|
|
67
|
+
for (const pattern of patterns) {
|
|
68
|
+
const match = content.match(pattern);
|
|
69
|
+
if (match) {
|
|
70
|
+
return match[1].trim().toLowerCase();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Fallback keyword detection
|
|
74
|
+
const lower = content.toLowerCase();
|
|
75
|
+
if (lower.includes('true'))
|
|
76
|
+
return 'true';
|
|
77
|
+
if (lower.includes('false'))
|
|
78
|
+
return 'false';
|
|
79
|
+
if (lower.includes('uncertain'))
|
|
80
|
+
return 'uncertain';
|
|
81
|
+
return 'unknown';
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Extract evidence/sources from response text
|
|
85
|
+
*/
|
|
86
|
+
export const extractEvidence = (content) => {
|
|
87
|
+
const evidence = [];
|
|
88
|
+
const patterns = [
|
|
89
|
+
/evidence:\s*([^\n]+)/gi,
|
|
90
|
+
/source:\s*([^\n]+)/gi,
|
|
91
|
+
/citation:\s*([^\n]+)/gi,
|
|
92
|
+
/\[(\d+)\]\s*([^\n]+)/g
|
|
93
|
+
];
|
|
94
|
+
for (const pattern of patterns) {
|
|
95
|
+
const matches = content.matchAll(pattern);
|
|
96
|
+
for (const match of matches) {
|
|
97
|
+
evidence.push(match[1] || match[2]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return evidence;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Extract confidence score from response text
|
|
104
|
+
*
|
|
105
|
+
* Patterns checked:
|
|
106
|
+
* - "confidence: 80%" / "certainty: 80%"
|
|
107
|
+
* - "80% confident"
|
|
108
|
+
* - Default: 0.5 (50%)
|
|
109
|
+
*/
|
|
110
|
+
export const extractConfidence = (content) => {
|
|
111
|
+
const patterns = [
|
|
112
|
+
/confidence:\s*(\d+)%?/i,
|
|
113
|
+
/certainty:\s*(\d+)%?/i,
|
|
114
|
+
/(\d+)%\s*confident/i
|
|
115
|
+
];
|
|
116
|
+
for (const pattern of patterns) {
|
|
117
|
+
const match = content.match(pattern);
|
|
118
|
+
if (match) {
|
|
119
|
+
return parseInt(match[1]) / 100;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return 0.5; // Default 50% confidence
|
|
123
|
+
};
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Consensus Analysis
|
|
126
|
+
// ============================================================================
|
|
127
|
+
/**
|
|
128
|
+
* Calculate consensus by clustering responses by conclusion
|
|
129
|
+
*
|
|
130
|
+
* Returns:
|
|
131
|
+
* - agreement: ratio of models in majority cluster
|
|
132
|
+
* - clusters: Map of conclusion → responses
|
|
133
|
+
* - majorityCluster: conclusion with most models
|
|
134
|
+
* - outlierModels: models not in majority cluster
|
|
135
|
+
*/
|
|
136
|
+
export const calculateConsensus = (config) => {
|
|
137
|
+
const clusters = new Map();
|
|
138
|
+
// Cluster responses by conclusion
|
|
139
|
+
for (const response of config.responses) {
|
|
140
|
+
const conclusion = response.conclusion || 'unknown';
|
|
141
|
+
if (!clusters.has(conclusion)) {
|
|
142
|
+
clusters.set(conclusion, []);
|
|
143
|
+
}
|
|
144
|
+
clusters.get(conclusion).push(response);
|
|
145
|
+
}
|
|
146
|
+
// Find majority cluster
|
|
147
|
+
const sortedClusters = Array.from(clusters.entries())
|
|
148
|
+
.sort((a, b) => b[1].length - a[1].length);
|
|
149
|
+
const majorityCluster = sortedClusters[0][0];
|
|
150
|
+
const majorityCount = sortedClusters[0][1].length;
|
|
151
|
+
const agreement = majorityCount / config.responses.length;
|
|
152
|
+
// Identify outliers (models not in majority cluster)
|
|
153
|
+
const outlierModels = sortedClusters
|
|
154
|
+
.slice(1)
|
|
155
|
+
.flatMap(([_, responses]) => responses.map(r => r.model));
|
|
156
|
+
return {
|
|
157
|
+
agreement,
|
|
158
|
+
clusters: new Map(clusters), // Convert to readonly
|
|
159
|
+
majorityCluster,
|
|
160
|
+
outlierModels
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Find outlier responses (dissenting views)
|
|
165
|
+
*/
|
|
166
|
+
export const findOutliers = (config) => {
|
|
167
|
+
return config.responses.filter(r => config.consensus.outlierModels.includes(r.model));
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Calculate overall confidence score
|
|
171
|
+
*
|
|
172
|
+
* Weighted formula:
|
|
173
|
+
* - 50% weight: consensus agreement
|
|
174
|
+
* - 30% weight: average model confidence
|
|
175
|
+
* - 20% weight: number of responses (more models = higher confidence)
|
|
176
|
+
*/
|
|
177
|
+
export const calculateConfidence = (config) => {
|
|
178
|
+
const agreementScore = config.consensus.agreement;
|
|
179
|
+
const avgModelConfidence = config.responses
|
|
180
|
+
.map(r => r.confidence || 0.5)
|
|
181
|
+
.reduce((a, b) => a + b, 0) / config.responses.length;
|
|
182
|
+
const responseCount = config.responses.length;
|
|
183
|
+
const responseScore = Math.min(responseCount / 5, 1); // Max at 5 models
|
|
184
|
+
return (agreementScore * 0.5 + avgModelConfidence * 0.3 + responseScore * 0.2);
|
|
185
|
+
};
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Synthesis
|
|
188
|
+
// ============================================================================
|
|
189
|
+
/**
|
|
190
|
+
* Generate beautiful verification report with table
|
|
191
|
+
*/
|
|
192
|
+
export const synthesizeVerifierReport = (config) => {
|
|
193
|
+
const majorityResponses = config.consensus.clusters.get(config.consensus.majorityCluster) || [];
|
|
194
|
+
const outlierCount = config.responses.length - majorityResponses.length;
|
|
195
|
+
const consensusPercent = (config.consensus.agreement * 100).toFixed(1);
|
|
196
|
+
let synthesis = `## 🔍 Multi-Model Verification Report\n\n`;
|
|
197
|
+
// Consensus indicator
|
|
198
|
+
synthesis += `### 📊 Consensus: ${consensusPercent}%\n\n`;
|
|
199
|
+
const consensusBar = Math.round(config.consensus.agreement * 10);
|
|
200
|
+
synthesis += `\`\`\`\n`;
|
|
201
|
+
synthesis += `[${'█'.repeat(consensusBar)}${'░'.repeat(10 - consensusBar)}] ${consensusPercent}% agreement\n`;
|
|
202
|
+
synthesis += `\`\`\`\n\n`;
|
|
203
|
+
// Model responses table
|
|
204
|
+
synthesis += `### 🤖 Model Responses\n\n`;
|
|
205
|
+
synthesis += buildResponseTable(config.responses, majorityResponses);
|
|
206
|
+
// Majority analysis
|
|
207
|
+
synthesis += `### 🎯 Majority View\n\n`;
|
|
208
|
+
synthesis += `**Conclusion:** ${config.consensus.majorityCluster}\n`;
|
|
209
|
+
synthesis += `**Models in agreement:** ${majorityResponses.length}/${config.responses.length}\n\n`;
|
|
210
|
+
if (majorityResponses.length > 0) {
|
|
211
|
+
synthesis += `**Key reasoning points:**\n`;
|
|
212
|
+
const points = extractKeyPoints(majorityResponses);
|
|
213
|
+
if (points.length > 0) {
|
|
214
|
+
points.forEach(point => synthesis += `- ${point}\n`);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// Fallback: show first sentence from each response
|
|
218
|
+
majorityResponses.slice(0, 3).forEach(resp => {
|
|
219
|
+
const firstSentence = resp.response.split(/[.!?]/)[0];
|
|
220
|
+
if (firstSentence && firstSentence.length > 10) {
|
|
221
|
+
synthesis += `- ${firstSentence.trim()}.\n`;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
synthesis += `\n`;
|
|
226
|
+
}
|
|
227
|
+
// Dissenting views
|
|
228
|
+
if (outlierCount > 0) {
|
|
229
|
+
synthesis += `### ⚠️ Dissenting Views (${outlierCount})\n\n`;
|
|
230
|
+
config.outliers.forEach(outlier => {
|
|
231
|
+
synthesis += `**${outlier.model}:** "${outlier.conclusion || 'unknown'}"\n`;
|
|
232
|
+
const preview = (outlier.response || '').substring(0, 150).replace(/\n/g, ' ');
|
|
233
|
+
if (preview) {
|
|
234
|
+
synthesis += `> ${preview}${outlier.response.length > 150 ? '...' : ''}\n\n`;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
// Summary
|
|
239
|
+
synthesis += `### 📋 Summary\n\n`;
|
|
240
|
+
synthesis += `\`\`\`\n`;
|
|
241
|
+
synthesis += `Total Models: ${config.responses.length}\n`;
|
|
242
|
+
synthesis += `Consensus: ${consensusPercent}%\n`;
|
|
243
|
+
synthesis += `Majority View: ${config.consensus.majorityCluster}\n`;
|
|
244
|
+
synthesis += `Agreeing Models: ${majorityResponses.length}\n`;
|
|
245
|
+
synthesis += `Dissenting: ${outlierCount}\n`;
|
|
246
|
+
synthesis += `High Confidence: ${config.consensus.agreement >= 0.8 ? 'YES ✓' : 'NO'}\n`;
|
|
247
|
+
synthesis += `\`\`\`\n`;
|
|
248
|
+
return synthesis;
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Format final verification result
|
|
252
|
+
*/
|
|
253
|
+
export const formatVerifierResult = (config) => {
|
|
254
|
+
return {
|
|
255
|
+
consensus: config.consensus,
|
|
256
|
+
majority: config.majority,
|
|
257
|
+
outliers: config.outliers,
|
|
258
|
+
responses: config.responses,
|
|
259
|
+
synthesis: config.synthesis,
|
|
260
|
+
confidence: config.confidence,
|
|
261
|
+
shouldTerminate: config.should_terminate
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
// ============================================================================
|
|
265
|
+
// Internal Utilities
|
|
266
|
+
// ============================================================================
|
|
267
|
+
/**
|
|
268
|
+
* Build response table with status icons
|
|
269
|
+
*/
|
|
270
|
+
const buildResponseTable = (responses, majorityResponses) => {
|
|
271
|
+
// Simple table format (without TableBuilder dependency)
|
|
272
|
+
let table = '| Status | Model | Conclusion | Confidence | Preview |\n';
|
|
273
|
+
table += '|:------:|:------|:----------:|-----------:|:--------|\n';
|
|
274
|
+
responses.forEach((resp) => {
|
|
275
|
+
const isMajority = majorityResponses.includes(resp);
|
|
276
|
+
const statusIcon = isMajority ? '✅' : '⚠️';
|
|
277
|
+
const conclusionIcon = resp.conclusion === 'true' ? '✓' :
|
|
278
|
+
resp.conclusion === 'false' ? '✗' :
|
|
279
|
+
resp.conclusion === 'uncertain' ? '❓' : '❔';
|
|
280
|
+
const confidence = resp.confidence ? `${Math.round(resp.confidence * 100)}%` : 'N/A';
|
|
281
|
+
const preview = (resp.response || '').substring(0, 60).replace(/\n/g, ' ').trim();
|
|
282
|
+
const previewText = preview ? `${preview}...` : 'No response';
|
|
283
|
+
table += `| ${statusIcon} | ${resp.model} | ${conclusionIcon} ${resp.conclusion || 'unknown'} | ${confidence} | ${previewText} |\n`;
|
|
284
|
+
});
|
|
285
|
+
table += '\n';
|
|
286
|
+
return table;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Extract key reasoning points from responses
|
|
290
|
+
*/
|
|
291
|
+
const extractKeyPoints = (responses) => {
|
|
292
|
+
const points = new Set();
|
|
293
|
+
for (const response of responses) {
|
|
294
|
+
const content = response.response || '';
|
|
295
|
+
const lines = content.split('\n')
|
|
296
|
+
.filter((line) => line.trim().startsWith('-') || line.trim().startsWith('•'))
|
|
297
|
+
.map((line) => line.replace(/^[-•]\s*/, '').trim())
|
|
298
|
+
.filter((line) => line.length > 10 && line.length < 200);
|
|
299
|
+
lines.forEach((line) => points.add(line));
|
|
300
|
+
}
|
|
301
|
+
return Array.from(points).slice(0, 5);
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Estimate tokens from text length
|
|
305
|
+
*/
|
|
306
|
+
const estimateTokens = (text) => {
|
|
307
|
+
return Math.floor(text.length / 4);
|
|
308
|
+
};
|
|
309
|
+
// ============================================================================
|
|
310
|
+
// Type Guards
|
|
311
|
+
// ============================================================================
|
|
312
|
+
/**
|
|
313
|
+
* Check if value is a valid ModelResponse
|
|
314
|
+
*/
|
|
315
|
+
export const isModelResponse = (value) => {
|
|
316
|
+
if (!value || typeof value !== 'object')
|
|
317
|
+
return false;
|
|
318
|
+
const obj = value;
|
|
319
|
+
return (typeof obj.model === 'string' &&
|
|
320
|
+
typeof obj.response === 'string');
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Check if value is a valid ConsensusAnalysis
|
|
324
|
+
*/
|
|
325
|
+
export const isConsensusAnalysis = (value) => {
|
|
326
|
+
if (!value || typeof value !== 'object')
|
|
327
|
+
return false;
|
|
328
|
+
const obj = value;
|
|
329
|
+
return (typeof obj.agreement === 'number' &&
|
|
330
|
+
typeof obj.majorityCluster === 'string' &&
|
|
331
|
+
Array.isArray(obj.outlierModels));
|
|
332
|
+
};
|