@waldzellai/clear-thought-onepointfive
Version:
Clear Thought MCP server with modular architecture - 38 reasoning operations for systematic thinking
1,200 lines (1,191 loc) • 107 kB
JavaScript
import { z } from "zod";
import { EphemeralNotebookStore } from "../notebook/EphemeralNotebook.js";
import { getPresetForPattern } from "../notebook/presets.js";
import { executePython } from "../utils/execution.js";
import { enhanceResponseWithNotebook } from "./notebookEnhancement.js";
// Initialize notebook store
const notebookStore = new EphemeralNotebookStore();
/**
* Helper function to generate dashboard HTML content
*/
function generateDashboardHTML(options) {
const { title, visualizationType, data, panels, layout, interactive } = options;
// Generate HTML with embedded Chart.js or D3.js visualization
const chartScript = visualizationType === "chart" ? `
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('mainChart').getContext('2d');
const chart = new Chart(ctx, {
type: '${data.chartType || "bar"}',
data: ${JSON.stringify(data.chartData || {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
datasets: [{
label: 'Dataset',
data: [12, 19, 3, 5, 2],
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
})},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: true },
tooltip: { enabled: ${interactive} }
}
}
});
</script>
` : "";
const panelsHTML = panels.map((panel, index) => `
<div class="panel" style="
padding: 15px;
margin: 10px;
border: 1px solid #ddd;
border-radius: 8px;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
">
<h3>${panel.title || `Panel ${index + 1}`}</h3>
<div class="panel-content">
${panel.content || `<p>Panel content for ${panel.type || 'metric'}</p>`}
${panel.value ? `<div class="metric-value" style="font-size: 2em; font-weight: bold; color: #2196F3;">${panel.value}</div>` : ''}
</div>
</div>
`).join('');
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
background: #f5f5f5;
}
.dashboard {
max-width: 1200px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.panels-container {
display: ${layout === 'grid' ? 'grid' : 'flex'};
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
flex-wrap: ${layout === 'flex' ? 'wrap' : 'nowrap'};
}
.chart-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
height: 400px;
margin-bottom: 20px;
}
canvas {
width: 100% !important;
height: 100% !important;
}
</style>
</head>
<body>
<div class="dashboard">
<div class="header">
<h1>${title}</h1>
<p>Interactive Dashboard - ${new Date().toLocaleString()}</p>
</div>
${visualizationType === 'chart' ? `
<div class="chart-container">
<canvas id="mainChart"></canvas>
</div>
` : ''}
<div class="panels-container">
${panelsHTML}
</div>
</div>
${chartScript}
${interactive ? `
<script>
// Enable interactive features
window.parent.postMessage({
type: 'ui-lifecycle-iframe-ready',
payload: { ready: true }
}, '*');
// Handle clicks on panels
document.querySelectorAll('.panel').forEach((panel, index) => {
panel.style.cursor = 'pointer';
panel.addEventListener('click', () => {
window.parent.postMessage({
type: 'notify',
payload: {
message: 'Panel ' + (index + 1) + ' clicked'
}
}, '*');
});
});
</script>
` : ''}
</body>
</html>
`;
}
/**
* Helper function to generate remote DOM script for dynamic content
*/
function generateRemoteDomScript(options) {
const { visualizationType, data, panels, interactive } = options;
return `
// Create dashboard container
const container = document.createElement('ui-container');
container.style.padding = '20px';
// Add title
const title = document.createElement('ui-text');
title.textContent = 'Dynamic Dashboard';
title.style.fontSize = '24px';
title.style.fontWeight = 'bold';
title.style.marginBottom = '20px';
container.appendChild(title);
// Add panels
${panels.map((panel, index) => `
const panel${index} = document.createElement('ui-panel');
panel${index}.style.padding = '15px';
panel${index}.style.margin = '10px';
panel${index}.style.border = '1px solid #ddd';
panel${index}.style.borderRadius = '8px';
const panelTitle${index} = document.createElement('ui-text');
panelTitle${index}.textContent = '${panel.title || `Panel ${index + 1}`}';
panelTitle${index}.style.fontWeight = 'bold';
panel${index}.appendChild(panelTitle${index});
${panel.value ? `
const panelValue${index} = document.createElement('ui-text');
panelValue${index}.textContent = '${panel.value}';
panelValue${index}.style.fontSize = '2em';
panelValue${index}.style.color = '#2196F3';
panel${index}.appendChild(panelValue${index});
` : ''}
${interactive ? `
panel${index}.style.cursor = 'pointer';
panel${index}.onclick = () => {
window.parent.postMessage({
type: 'tool',
payload: {
toolName: 'handlePanelClick',
params: { panelId: ${index} }
}
}, '*');
};
` : ''}
container.appendChild(panel${index});
`).join('\n')}
// Append to root
root.appendChild(container);
// Send ready signal
window.parent.postMessage({
type: 'ui-lifecycle-iframe-ready',
payload: { ready: true }
}, '*');
`;
}
/**
* Registers the unified Clear Thought tool with the MCP server
*
* This single tool provides access to all reasoning operations through
* an operation parameter, following the Toolhost pattern.
*
* @param server - The MCP server instance
* @param sessionState - The session state manager
*/
export const ClearThoughtParamsSchema = z.object({
operation: z
.enum([
// Core thinking operations
"sequential_thinking",
"mental_model",
"debugging_approach",
"creative_thinking",
"visual_reasoning",
"metacognitive_monitoring",
"scientific_method",
// Collaborative operations
"collaborative_reasoning",
"decision_framework",
"socratic_method",
"structured_argumentation",
// Systems and session operations
"systems_thinking",
"session_info",
"session_export",
"session_import",
// Deep reasoning operations
"pdr_reasoning",
// New modules
"research",
"analogical_reasoning",
"causal_analysis",
"statistical_reasoning",
"simulation",
"optimization",
"ethical_analysis",
"visual_dashboard",
"custom_framework",
"code_execution",
// Reasoning pattern operations
"tree_of_thought",
"beam_search",
"mcts",
"graph_of_thought",
"orchestration_suggest",
// Metagame operations
"ooda_loop",
"ulysses_protocol",
// Notebook operations
"notebook_create",
"notebook_add_cell",
"notebook_run_cell",
"notebook_export",
])
.describe("What type of reasoning operation to perform"),
// Common parameters
prompt: z.string().describe("The problem, question, or challenge to work on"),
context: z
.string()
.optional()
.describe("Additional context or background information"),
sessionId: z
.string()
.optional()
.describe("Session identifier for continuity"),
// Operation-specific parameters (will be validated based on operation)
parameters: z
.record(z.string(), z.unknown())
.optional()
.describe("Operation-specific parameters"),
// Advanced options
advanced: z
.object({
autoProgress: z
.boolean()
.optional()
.describe("Automatically progress through stages when applicable"),
saveToSession: z
.boolean()
.default(true)
.describe("Save results to session state"),
generateNextSteps: z
.boolean()
.default(true)
.describe("Generate recommended next steps"),
})
.optional()
.describe("Advanced reasoning options"),
});
export async function handleClearThoughtTool(sessionState, args) {
const startTime = Date.now();
try {
// Special handling for code execution to allow real run
if (args.operation === "code_execution") {
const params = (args.parameters || {});
const lang = params.language || "python";
const code = String(params.code || "");
const cfg = sessionState.getConfig();
if (lang !== "python" || !cfg.allowCodeExecution) {
const preview = await executeClearThoughtOperation(sessionState, args.operation, { prompt: args.prompt, parameters: args.parameters });
return {
content: [{ type: "text", text: JSON.stringify(preview, null, 2) }],
};
}
const result = await executePython(code, cfg.pythonCommand, cfg.executionTimeoutMs);
const executionResult = { toolOperation: "code_execution", ...result };
return {
content: [
{
type: "text",
text: JSON.stringify(executionResult, null, 2),
},
],
};
}
// Auto-seed most operations with a brief sequential_thinking step
const seedExclusions = new Set([
"sequential_thinking",
"code_execution",
"session_info",
"session_export",
"session_import",
]);
const shouldSeed = !seedExclusions.has(args.operation);
// Handle async operations
if (args.operation === "notebook_run_cell") {
const params = (args.parameters || {});
try {
const execution = await notebookStore.executeCell(params.notebookId || "", params.cellId || "", params.timeoutMs || 5000);
const notebookResult = {
toolOperation: "notebook_run_cell",
notebookId: params.notebookId,
cellId: params.cellId,
execution: {
id: execution.id,
status: execution.status,
outputs: execution.outputs,
error: execution.error,
duration: execution.completedAt
? execution.completedAt - execution.startedAt
: undefined,
},
};
return {
content: [
{
type: "text",
text: JSON.stringify(notebookResult, null, 2),
},
],
};
}
catch (error) {
const errorResult = {
toolOperation: "notebook_run_cell",
notebookId: params.notebookId,
cellId: params.cellId,
error: error.message,
success: false,
};
return {
content: [
{
type: "text",
text: JSON.stringify(errorResult, null, 2),
},
],
};
}
}
// Execute the main operation
const result = await executeClearThoughtOperation(sessionState, args.operation, { prompt: args.prompt, parameters: args.parameters });
const enriched = shouldSeed
? {
...result,
initialThought: await executeClearThoughtOperation(sessionState, "sequential_thinking", {
prompt: `Plan approach for: ${args.prompt}`,
parameters: {
thoughtNumber: 1,
totalThoughts: 3,
nextThoughtNeeded: true,
needsMoreThoughts: true,
pattern: "chain",
},
}),
}
: result;
// Enhance response with notebook resources if applicable
const baseResponse = {
content: [{ type: "text", text: JSON.stringify(enriched, null, 2) }],
};
const enhancedResponse = enhanceResponseWithNotebook(baseResponse, args.operation, args.prompt);
return enhancedResponse;
}
catch (error) {
const errorResponse = {
toolOperation: args.operation,
error: error.message,
success: false
};
return {
content: [
{
type: "text",
text: JSON.stringify(errorResponse, null, 2),
},
],
isError: true,
};
}
}
// Backwards-compatible registration helper (kept for compatibility; unused by low-level Server)
export function registerTools(server, sessionState) {
server.tool("clear_thought", "Unified Clear Thought reasoning tool - provides all reasoning operations through a single interface", ClearThoughtParamsSchema.shape, async (args) => handleClearThoughtTool(sessionState, args));
}
/**
* Unified Clear Thought reasoning operations
*
* This module provides all reasoning operations through a single interface,
* following the websetsManager pattern without external dependencies.
*
* @param sessionState - The session state manager
* @param operation - The operation to perform
* @param args - Operation arguments
*/
export async function executeClearThoughtOperation(sessionState, operation, args) {
const { prompt, parameters = {} } = args;
// Optional reasoning pattern selection for sequential_thinking
const specifiedPattern = parameters.pattern;
const patternParams = parameters.patternParams || {};
const selectReasoningPattern = () => {
if (specifiedPattern && specifiedPattern !== "auto")
return specifiedPattern;
// Heuristic selection from prompt/params
const ptext = `${prompt}`.toLowerCase();
if ("depth" in patternParams ||
"breadth" in patternParams ||
ptext.includes("branch") ||
ptext.includes("options")) {
return "tree";
}
if ("beamWidth" in patternParams ||
ptext.includes("candidates") ||
ptext.includes("top-k")) {
return "beam";
}
if ("simulations" in patternParams ||
ptext.includes("uncertain") ||
ptext.includes("probability") ||
ptext.includes("stochastic")) {
return "mcts";
}
if ("nodes" in patternParams ||
"edges" in patternParams ||
ptext.includes("dependencies") ||
ptext.includes("graph")) {
return "graph";
}
return "chain";
};
// Type guard to ensure parameters are properly typed
const getParam = (key, defaultValue) => {
return parameters[key] ?? defaultValue;
};
// Unified handler for all operations
switch (operation) {
case "sequential_thinking": {
// Choose reasoning pattern (default 'chain') and optionally dispatch
const chosenPattern = selectReasoningPattern();
const thoughtData = {
thought: prompt,
thoughtNumber: parameters.thoughtNumber || 1,
totalThoughts: parameters.totalThoughts || 1,
nextThoughtNeeded: parameters.nextThoughtNeeded || false,
isRevision: parameters.isRevision,
revisesThought: parameters.revisesThought,
branchFromThought: parameters.branchFromThought,
branchId: parameters.branchId,
needsMoreThoughts: parameters.needsMoreThoughts,
};
const added = sessionState.addThought(thoughtData);
const allThoughts = sessionState.getThoughts();
const recentThoughts = allThoughts.slice(-3);
// If a non-chain pattern is selected, optionally execute the corresponding pattern operation
let patternResult;
if (chosenPattern !== "chain" &&
!parameters.__disablePatternDispatch) {
const opMap = {
tree: "tree_of_thought",
beam: "beam_search",
mcts: "mcts",
graph: "graph_of_thought",
};
const mappedOp = opMap[chosenPattern];
if (mappedOp) {
patternResult = await executeClearThoughtOperation(sessionState, mappedOp, { prompt, parameters: patternParams });
}
}
return {
toolOperation: "sequential_thinking",
selectedPattern: chosenPattern,
patternResult,
...thoughtData,
status: added ? "success" : "limit_reached",
sessionContext: {
sessionId: sessionState.sessionId,
totalThoughts: allThoughts.length,
remainingThoughts: sessionState.getRemainingThoughts(),
recentThoughts: recentThoughts.map((t) => ({
thoughtNumber: t.thoughtNumber,
isRevision: t.isRevision,
})),
},
};
}
case "mental_model": {
const modelData = {
modelName: parameters.model || "first_principles",
problem: prompt,
steps: parameters.steps || [],
reasoning: parameters.reasoning || "",
conclusion: parameters.conclusion || "",
};
sessionState.addMentalModel(modelData);
const allModels = sessionState.getMentalModels();
return {
toolOperation: "mental_model",
...modelData,
sessionContext: {
sessionId: sessionState.sessionId,
totalModels: allModels.length,
recentModels: allModels
.slice(-3)
.map((m) => ({ modelName: m.modelName, problem: m.problem })),
},
};
}
case "debugging_approach": {
const debugData = {
approachName: parameters.approach || "binary_search",
issue: prompt,
steps: parameters.steps || [],
findings: parameters.findings || "",
resolution: parameters.resolution || "",
};
sessionState.addDebuggingSession(debugData);
const allSessions = sessionState.getDebuggingSessions();
return {
toolOperation: "debugging_approach",
...debugData,
sessionContext: {
sessionId: sessionState.sessionId,
totalSessions: allSessions.length,
recentSessions: allSessions
.slice(-3)
.map((s) => ({ approachName: s.approachName, issue: s.issue })),
},
};
}
case "creative_thinking": {
/**
* Creative Thinking Operation
*
* Generates idea seeds and connections using simple combinatorial techniques.
*
* Expected in prompt: Creative challenge or problem to brainstorm
*
* Expected in parameters (model should provide these or they'll be generated):
* - techniques?: string[] - Creative techniques to apply (default: ["brainstorming", "SCAMPER"])
* - numIdeas?: number - Number of ideas to generate (default: 8)
* - ideas?: string[] - Pre-existing ideas to build upon
*
* The model should either provide structured ideas or the system will generate them using combinatorial techniques.
*/
// Use provided parameters or generate ideas
let ideas = parameters.ideas || [];
const techniques = parameters.techniques || ["brainstorming", "SCAMPER"];
const numIdeas = getParam("numIdeas", 8);
// Generate ideas if none provided
if (ideas.length === 0 && prompt) {
// Extract key tokens from prompt
const tokens = prompt.toLowerCase()
.replace(/[^a-z\s]/g, '')
.split(/\s+/)
.filter(t => t.length > 2);
// SCAMPER verbs for idea generation
const scamperVerbs = [
"substitute", "combine", "adapt", "modify",
"put to other use", "eliminate", "reverse"
];
// Generate ideas by combining tokens with techniques
for (let i = 0; i < numIdeas && tokens.length > 0; i++) {
const token = tokens[i % tokens.length];
const verb = scamperVerbs[i % scamperVerbs.length];
const otherToken = tokens[(i + 1) % tokens.length];
ideas.push(`${verb} ${token} with ${otherToken}`);
}
}
// Generate connections between concepts
let connections = parameters.connections || [];
if (connections.length === 0 && ideas.length > 1) {
// Create connections between ideas
for (let i = 0; i < Math.min(ideas.length - 1, 3); i++) {
connections.push(`${ideas[i]} could lead to ${ideas[i + 1]}`);
}
}
// Generate insights (top-ranked ideas by novelty and coverage)
let insights = parameters.insights || [];
if (insights.length === 0 && ideas.length > 0) {
// Simple ranking: pick ideas with rare tokens (novelty) and prompt overlap (coverage)
const promptTokens = new Set(prompt.toLowerCase().split(/\s+/));
const rankedIdeas = ideas
.map(idea => {
const ideaTokens = idea.toLowerCase().split(/\s+/);
const coverage = ideaTokens.filter(t => promptTokens.has(t)).length / ideaTokens.length;
const novelty = 1 - coverage; // Simple novelty heuristic
const score = (coverage + novelty) / 2;
return { idea, score };
})
.sort((a, b) => b.score - a.score)
.slice(0, 3)
.map(item => item.idea);
insights = rankedIdeas;
}
const creativeData = {
prompt: prompt,
ideas,
techniques,
connections,
insights,
sessionId: `creative-${Date.now()}`,
iteration: getParam("iteration", 1),
nextIdeaNeeded: getParam("nextIdeaNeeded", false),
};
sessionState.addCreativeSession(creativeData);
const allSessions = sessionState.getCreativeSessions();
return {
toolOperation: "creative_thinking",
...creativeData,
sessionContext: {
sessionId: sessionState.sessionId,
totalSessions: allSessions.length,
recentSessions: allSessions
.slice(-3)
.map((s) => ({ prompt: s.prompt, techniques: s.techniques })),
},
};
}
case "visual_reasoning": {
const visualData = {
operation: "create",
diagramId: getParam("diagramId", `diagram-${Date.now()}`),
diagramType: getParam("diagramType", "flowchart"),
iteration: getParam("iteration", 1),
nextOperationNeeded: getParam("nextOperationNeeded", false),
};
sessionState.addVisualOperation(visualData);
const allOperations = sessionState.getVisualOperations();
return {
toolOperation: "visual_reasoning",
...visualData,
sessionContext: {
sessionId: sessionState.sessionId,
totalOperations: allOperations.length,
recentOperations: allOperations
.slice(-3)
.map((v) => ({
diagramType: v.diagramType,
operation: v.operation,
})),
},
};
}
case "metacognitive_monitoring": {
/**
* Metacognitive Monitoring Operation
*
* Records stage, uncertainty areas, and recommended approach; suggests assessments.
*
* Expected in prompt: Task or problem being monitored
*
* Expected in parameters:
* - stage?: 'planning' | 'monitoring' | 'evaluating' | 'reflecting'
* - uncertaintyAreas?: string[] - Areas of uncertainty
* - overallConfidence?: number - Confidence level (0-1)
* - recommendedApproach?: string - Suggested approach
*/
const stage = getParam("stage", "monitoring");
const uncertaintyAreas = getParam("uncertaintyAreas", []);
const overallConfidence = getParam("overallConfidence", 0.5);
const recommendedApproach = getParam("recommendedApproach", "");
// Suggest assessments based on stage
let suggestedAssessments = [];
if (stage === "monitoring") {
suggestedAssessments = ["knowledge", "progress", "overall"];
}
else if (stage === "evaluating") {
suggestedAssessments = ["effectiveness", "efficiency", "completeness"];
}
else if (stage === "reflecting") {
suggestedAssessments = ["lessons-learned", "improvements", "next-steps"];
}
const metaData = {
task: prompt,
stage,
overallConfidence,
uncertaintyAreas,
recommendedApproach,
suggestedAssessments,
monitoringId: `meta-${Date.now()}`,
iteration: getParam("iteration", 1),
nextAssessmentNeeded: suggestedAssessments.length > 0,
};
sessionState.addMetacognitive(metaData);
sessionState.updateKPI('overall_confidence', overallConfidence);
const allSessions = sessionState.getMetacognitiveSessions();
return {
toolOperation: "metacognitive_monitoring",
...metaData,
sessionContext: {
sessionId: sessionState.sessionId,
totalSessions: allSessions.length,
recentSessions: allSessions
.slice(-3)
.map((m) => ({ task: m.task, stage: m.stage })),
},
};
}
case "scientific_method": {
const scientificData = {
stage: getParam("stage", "hypothesis"),
inquiryId: `sci-${Date.now()}`,
iteration: getParam("iteration", 1),
nextStageNeeded: getParam("nextStageNeeded", false),
};
sessionState.addScientificInquiry(scientificData);
const allInquiries = sessionState.getScientificInquiries();
return {
toolOperation: "scientific_method",
...scientificData,
sessionContext: {
sessionId: sessionState.sessionId,
totalInquiries: allInquiries.length,
recentInquiries: allInquiries
.slice(-3)
.map((s) => ({ stage: s.stage })),
},
};
}
case "collaborative_reasoning": {
/**
* Collaborative Reasoning Operation
*
* Maintains personas and contributions; suggests next contribution types.
*
* Expected in prompt: Topic or problem for collaborative analysis
*
* Expected in parameters:
* - personas?: Array<{id: string, name: string, expertise: string[]}>
* - contributions?: Array<{personaId: string, content: string, type: string}>
* - stage?: 'problem-definition' | 'exploration' | 'synthesis' | 'conclusion'
*/
const personas = parameters.personas || [];
let contributions = parameters.contributions || [];
const stage = getParam("stage", "problem-definition");
// Append a synthetic contribution from prompt if none provided
if (contributions.length === 0 && prompt) {
contributions.push({
personaId: "system",
content: prompt,
type: "observation",
confidence: 0.8
});
}
// Suggest next contribution types based on stage
let suggestedContributionTypes = [];
switch (stage) {
case "problem-definition":
suggestedContributionTypes = ["question", "concern", "observation"];
break;
case "exploration":
suggestedContributionTypes = ["insight", "suggestion", "challenge"];
break;
case "synthesis":
suggestedContributionTypes = ["synthesis", "insight", "question"];
break;
case "conclusion":
suggestedContributionTypes = ["synthesis", "concern", "observation"];
break;
}
const collaborativeData = {
topic: prompt,
personas,
contributions,
stage,
suggestedContributionTypes,
nextContributionNeeded: contributions.length < 3,
sessionId: `collab-${Date.now()}`,
};
return {
toolOperation: "collaborative_reasoning",
...collaborativeData,
sessionContext: {
sessionId: sessionState.sessionId,
stats: sessionState.getStats(),
},
};
}
case "decision_framework": {
/**
* Decision Framework Operation
*
* Accepts options/criteria and computes expected-utility or multi-criteria scores.
*
* Expected in prompt: Decision to be made
*
* Expected in parameters:
* - options: Array<{id: string, name: string, attributes?: Record<string, any>}>
* - criteria: Array<{name: string, weight: number, type: 'maximize'|'minimize'}>
* - possibleOutcomes?: Array<{option: string, probability: number, value: number}>
* - analysisType: 'expected-utility' | 'multi-criteria'
*/
const options = parameters.options || [];
const criteria = parameters.criteria || [];
const possibleOutcomes = parameters.possibleOutcomes || [];
const analysisType = getParam("analysisType", "multi-criteria");
let result = {};
if (analysisType === "expected-utility" && possibleOutcomes.length > 0) {
// Calculate expected values for each option
const expectedValues = {};
options.forEach(opt => {
const outcomes = possibleOutcomes.filter(o => o.option === opt.id || o.option === opt.name);
expectedValues[opt.id || opt.name] = outcomes.reduce((sum, o) => sum + (o.probability * o.value), 0);
});
result.expectedValues = expectedValues;
const bestOption = Object.entries(expectedValues).reduce((best, [key, val]) => val > best.value ? { id: key, value: val } : best, { id: "", value: -Infinity });
result.recommendation = bestOption.id;
}
else if (analysisType === "multi-criteria" && criteria.length > 0) {
// Multi-criteria scoring
const scores = {};
const totalWeight = criteria.reduce((sum, c) => sum + (c.weight || 1), 0);
options.forEach(opt => {
let score = 0;
criteria.forEach(criterion => {
const value = opt.attributes?.[criterion.name] || 0;
const normalizedWeight = (criterion.weight || 1) / totalWeight;
score += value * normalizedWeight;
});
scores[opt.id || opt.name] = score;
});
result.multiCriteriaScores = scores;
const bestOption = Object.entries(scores).reduce((best, [key, val]) => val > best.value ? { id: key, value: val } : best, { id: "", value: -Infinity });
result.recommendation = bestOption.id;
}
const decisionData = {
decisionStatement: prompt,
options,
criteria,
possibleOutcomes,
analysisType,
...result,
suggestedNextStage: result.recommendation ? "implementation" : "gather-more-data",
decisionId: `decision-${Date.now()}`,
};
return {
toolOperation: "decision_framework",
...decisionData,
sessionContext: {
sessionId: sessionState.sessionId,
stats: sessionState.getStats(),
},
};
}
case "socratic_method": {
/**
* Socratic Method Operation
*
* Builds claims/premises/conclusion structures through questioning.
*
* Expected in prompt: Initial question or claim to examine
*
* Expected in parameters:
* - claim?: string - The claim being examined
* - premises?: string[] - Supporting premises
* - stage?: 'clarification' | 'assumptions' | 'reasons' | 'viewpoints' | 'consequences'
*/
const claim = getParam("claim", "");
let premises = parameters.premises || [];
const stage = getParam("stage", "clarification");
// Extract premises from prompt if not provided
if (premises.length === 0 && prompt) {
// Look for numbered or bulleted reasons
const lines = prompt.split(/\n/);
lines.forEach(line => {
if (/^[\d\-\*•]/.test(line.trim())) {
premises.push(line.replace(/^[\d\-\*•\.\)]+\s*/, '').trim());
}
});
}
// Calculate confidence based on premise strength
const strengthKeywords = ['clearly', 'obviously', 'certainly', 'definitely'];
const weaknessKeywords = ['maybe', 'perhaps', 'possibly', 'might'];
let confidence = 0.5;
const allText = `${claim} ${premises.join(' ')}`.toLowerCase();
strengthKeywords.forEach(word => {
if (allText.includes(word))
confidence += 0.1;
});
weaknessKeywords.forEach(word => {
if (allText.includes(word))
confidence -= 0.1;
});
confidence = Math.max(0, Math.min(1, confidence));
const socraticData = {
question: prompt,
claim,
premises,
conclusion: parameters.conclusion || "",
argumentType: parameters.argumentType || "deductive",
confidence,
stage,
nextArgumentNeeded: premises.length < 2,
sessionId: `socratic-${Date.now()}`,
};
return {
toolOperation: "socratic_method",
...socraticData,
sessionContext: {
sessionId: sessionState.sessionId,
stats: sessionState.getStats(),
},
};
}
case "structured_argumentation": {
const argumentData = {
claim: prompt,
premises: parameters.premises || [],
conclusion: parameters.conclusion || "",
argumentType: parameters.argumentType || "deductive",
confidence: parameters.confidence || 0.5,
respondsTo: parameters.respondsTo,
supports: parameters.supports || [],
contradicts: parameters.contradicts || [],
strengths: parameters.strengths || [],
weaknesses: parameters.weaknesses || [],
relevance: parameters.relevance || 0.5,
sessionId: `arg-${Date.now()}`,
iteration: parameters.iteration || 1,
nextArgumentNeeded: parameters.nextArgumentNeeded || false,
};
return {
toolOperation: "structured_argumentation",
...argumentData,
sessionContext: {
sessionId: sessionState.sessionId,
stats: sessionState.getStats(),
},
};
}
case "systems_thinking": {
/**
* Systems Thinking Operation
*
* Analyzes complex systems by identifying components, relationships, and feedback loops.
*
* Expected in prompt: A description of the system to analyze
*
* Expected in parameters (model should provide these based on analysis):
* - components: string[] - Key elements/entities in the system
* - relationships: Array<{from: string, to: string, type: string, strength?: number}> - How components interact
* - feedbackLoops: Array<{components: string[], type: 'positive'|'negative', description: string}> - Reinforcing or balancing loops
* - emergentProperties: string[] - Properties that arise from system interactions
* - leveragePoints: string[] - High-impact intervention opportunities
*
* The model should analyze the system described in the prompt and structure it into these components.
*/
// Use provided parameters or default to empty arrays
const components = parameters.components || [];
const relationships = parameters.relationships || [];
const feedbackLoops = parameters.feedbackLoops || [];
const emergentProperties = parameters.emergentProperties || [];
const leveragePoints = parameters.leveragePoints || [];
// Validate and detect feedback loops from relationships if not provided
if (feedbackLoops.length === 0 && relationships.length > 0) {
const graph = new Map();
relationships.forEach((rel) => {
if (!graph.has(rel.from))
graph.set(rel.from, new Set());
graph.get(rel.from).add(rel.to);
});
// Simple cycle detection (depth 2-3)
for (const [start, targets] of graph.entries()) {
for (const mid of targets) {
if (graph.has(mid)) {
for (const end of graph.get(mid)) {
if (end === start) {
// Found 2-cycle
feedbackLoops.push({
components: [start, mid],
type: 'negative',
description: `${start} -> ${mid} -> ${start}`
});
}
else if (graph.has(end) && graph.get(end).has(start)) {
// Found 3-cycle
feedbackLoops.push({
components: [start, mid, end],
type: 'positive',
description: `${start} -> ${mid} -> ${end} -> ${start}`
});
}
}
}
}
}
}
const systemsData = {
system: prompt,
components,
relationships,
feedbackLoops,
emergentProperties,
leveragePoints,
sessionId: `systems-${Date.now()}`,
iteration: parameters.iteration || 1,
nextAnalysisNeeded: parameters.nextAnalysisNeeded || false,
};
// Update KPI for systems components
if (components.length > 0) {
sessionState.updateKPI('systems_components_count', components.length);
}
return {
toolOperation: "systems_thinking",
...systemsData,
sessionContext: {
sessionId: sessionState.sessionId,
stats: sessionState.getStats(),
},
};
}
case "session_info": {
return {
toolOperation: "session_info",
sessionId: sessionState.sessionId,
stats: sessionState.getStats(),
};
}
case "session_export": {
return {
toolOperation: "session_export",
sessionData: sessionState.export(),
};
}
case "session_import": {
return {
toolOperation: "session_import",
result: "Session import completed",
};
}
// -------------------- New modules --------------------
case "pdr_reasoning": {
// PDR uses sequential thinking with progressive refinement pattern
return await executeClearThoughtOperation(sessionState, "sequential_thinking", {
prompt,
parameters: {
...parameters,
pattern: "chain",
patternParams: {
depth: 3,
breadth: 2,
},
},
});
}
case "research": {
/**
* Research Operation
*
* Structures research intent and placeholders for downstream web tooling (no external calls here).
*
* Expected in prompt: Research question or topic to investigate
*
* Expected in parameters (model should provide these):
* - subqueries?: string[] - Specific sub-questions to research
* - findings?: ResearchFinding[] - Any pre-existing findings
* - citations?: ResearchSource[] - Any pre-existing sources
*
* The model should break down the research prompt into specific, searchable questions.
*/
const subqueries = parameters.subqueries || [];
let findings = parameters.findings || [];
let citations = parameters.citations || [];
// If no structured input, create placeholder findings from prompt
if (findings.length === 0 && prompt) {
// Split prompt into claims and questions
const sentences = prompt.split(/[.!?]+/).filter(s => s.trim().length > 0);
for (const sentence of sentences.slice(0, 3)) { // Limit to first 3 sentences
const trimmed = sentence.trim();
if (trimmed) {
// Create placeholder finding
findings.push({
claim: `Research needed: ${trimmed}`,
evidence: "[Evidence to be gathered]",
confidence: 0.0, // No evidence yet
sources: [] // No sources yet
});
}
}
}
// Generate sub-queries if none provided
let derivedSubqueries = subqueries;
if (derivedSubqueries.length === 0 && prompt) {
// Simple heuristic: look for question words and create variants
const questionStarters = ['What', 'How', 'Why', 'When', 'Where', 'Who'];
const keywords = prompt.split(/\s+/).filter(w => w.length > 3).slice(0, 3);
for (const starter of questionStarters.slice(0, 3)) {
for (const keyword of keywords.slice(0, 2)) {
derivedSubqueries.push(`${starter} ${keyword.toLowerCase()}?`);
}
}
derivedSubqueries = derivedSubqueries.slice(0, 5); // Limit to 5 subqueries
}
const result = {
query: prompt,
findings,
citations,
};
return {
toolOperation: "research",
...result,
subqueries: derivedSubqueries,
note: "This operation structures research intent. Use external tools for actual web search and data gathering."
};
}
case "analogical_reasoning": {
/**
* Analogical Reasoning Operation
*
* Maps source→target concept roles using pattern templates.
*
* Expected in prompt: Description of analogy or domains to compare
*
* Expected in parameters (model should provide these):
* - sourceDomain: string - The familiar domain to map from
* - targetDomain: string - The unfamiliar domain to map to
* - mappings?: AnalogyMapping[] - Explicit concept mappings
* - inferredInsights?: string[] - Insights derived from the analogy
*
* The model should identify the two domains being compared and map concepts between them.
*/
let sourceDomain = getParam("sourceDomain", "");
let targetDomain = getParam("targetDomain", "");
let mappings = parameters.mappings || [];
let inferredInsights = parameters.inferredInsights || [];
// If domains not provided, try to extract from prompt
if (!sourceDomain || !targetDomain) {
// Look for "X is like Y" patterns
const analogyPatterns = [
/([\w\s]+)\s+is\s+like\s+([\w\s]+)/gi,
/([\w\s]+)\s+resembles\s+([\w\s]+)/gi,
/([\w\s]+)\s+similar\s+to\s+([\w\s]+)/gi,
/compare\s+([\w\s]+)\s+(?:to|with)\s+([\w\s]+)/gi
];
for (const pattern of analogyPatterns) {
const match = pattern.exec(prompt);
if (match) {
sourceDomain = match[1].trim();
targetDomain = match[2].trim();
break;
}
}
// Fallback: split on common separators
if (!sourceDomain || !targetDomain) {
const parts = prompt.split(/\s+(?:and|vs|versus|compared to)\s+/i);
if (parts.length >= 2) {
sourceDomain = parts[0].trim();
targetDomain = parts[1].trim();
}
}
}
// Generate mappings if none provided and both domains exist
if (mappings.length === 0 && sourceDomain && targetDomain) {
// Extract core nouns from both domains
const extractNouns = (text) => {
return text.toLowerCase()
.replace(/[^a-z\s]/g, '')
.split(/\s+/)
.filter(word => word.length > 2