@gotza02/sequential-thinking 2026.3.10 ā 2026.3.12
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/dist/core/ContextManager.d.ts +12 -0
- package/dist/core/ContextManager.js +143 -0
- package/dist/core/RuleManager.d.ts +10 -0
- package/dist/core/RuleManager.js +62 -0
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.js +67 -0
- package/dist/index.js +6 -0
- package/dist/lib.d.ts +3 -0
- package/dist/lib.js +39 -8
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ThoughtData } from '../lib.js';
|
|
2
|
+
export declare class ContextManager {
|
|
3
|
+
private summaryCache;
|
|
4
|
+
private readonly STOP_WORDS;
|
|
5
|
+
constructor();
|
|
6
|
+
getOptimizedContext(history: ThoughtData[], currentBlockId: string | null): Promise<string>;
|
|
7
|
+
private getBlockSummaries;
|
|
8
|
+
private selectRelevantBlocks;
|
|
9
|
+
private extractKeywords;
|
|
10
|
+
private calculateOverlap;
|
|
11
|
+
private generateSummary;
|
|
12
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
export class ContextManager {
|
|
2
|
+
summaryCache = new Map();
|
|
3
|
+
STOP_WORDS = new Set([
|
|
4
|
+
'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by',
|
|
5
|
+
'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
6
|
+
'have', 'has', 'had', 'do', 'does', 'did',
|
|
7
|
+
'it', 'this', 'that', 'these', 'those',
|
|
8
|
+
'i', 'you', 'he', 'she', 'we', 'they',
|
|
9
|
+
'from', 'as', 'what', 'which', 'when', 'where', 'how', 'why'
|
|
10
|
+
]);
|
|
11
|
+
constructor() { }
|
|
12
|
+
async getOptimizedContext(history, currentBlockId) {
|
|
13
|
+
const safeCurrentBlockId = currentBlockId || '';
|
|
14
|
+
const oldBlocksThoughts = history.filter(t => t.blockId !== safeCurrentBlockId);
|
|
15
|
+
const activeBlock = history.filter(t => t.blockId === safeCurrentBlockId);
|
|
16
|
+
// 1. Get all summaries (with caching)
|
|
17
|
+
const allSummaries = await this.getBlockSummaries(oldBlocksThoughts);
|
|
18
|
+
// 2. Select relevant summaries (Smart Context)
|
|
19
|
+
// Query = Active block content + Current Topic (if any)
|
|
20
|
+
const currentTopic = activeBlock.length > 0 ? activeBlock[0].thought.substring(0, 50) : '';
|
|
21
|
+
const queryText = currentTopic + " " + activeBlock.map(t => t.thought).join(' ');
|
|
22
|
+
const selectedSummaries = this.selectRelevantBlocks(allSummaries, queryText);
|
|
23
|
+
// 3. Format Output
|
|
24
|
+
let summariesText = "";
|
|
25
|
+
if (selectedSummaries.length === 0 && allSummaries.length > 0) {
|
|
26
|
+
// Fallback: If no relevant found (rare), show last 2
|
|
27
|
+
const recent = allSummaries.sort((a, b) => b.lastUpdated - a.lastUpdated).slice(0, 2);
|
|
28
|
+
summariesText = recent.map(s => `[Block: ${s.blockId}] Summary: ${s.summary}`).join('\n');
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
summariesText = selectedSummaries.map(s => `[Block: ${s.blockId}] Summary: ${s.summary}`).join('\n');
|
|
32
|
+
}
|
|
33
|
+
// Format active block detailed
|
|
34
|
+
const activeContext = activeBlock.map(t => `[${t.thoughtType?.toUpperCase() || 'INFO'}] #${t.thoughtNumber}: ${t.thought}`).join('\n');
|
|
35
|
+
return `
|
|
36
|
+
=== PROJECT HISTORY (SMART CONTEXT) ===
|
|
37
|
+
${summariesText}
|
|
38
|
+
|
|
39
|
+
=== CURRENT FOCUS (DETAILED: ${safeCurrentBlockId || 'GLOBAL'}) ===
|
|
40
|
+
${activeContext}
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
async getBlockSummaries(logs) {
|
|
44
|
+
// 1. Group by block (maintaining order)
|
|
45
|
+
const groups = new Map();
|
|
46
|
+
logs.forEach((log, index) => {
|
|
47
|
+
const bid = log.blockId || 'default';
|
|
48
|
+
if (!groups.has(bid)) {
|
|
49
|
+
groups.set(bid, { thoughts: [], maxIndex: -1 });
|
|
50
|
+
}
|
|
51
|
+
const group = groups.get(bid);
|
|
52
|
+
group.thoughts.push(log);
|
|
53
|
+
group.maxIndex = index; // Always updates to the latest index encountered
|
|
54
|
+
});
|
|
55
|
+
const results = [];
|
|
56
|
+
for (const [blockId, data] of groups) {
|
|
57
|
+
if (!blockId)
|
|
58
|
+
continue;
|
|
59
|
+
const { thoughts, maxIndex } = data;
|
|
60
|
+
let summary = "";
|
|
61
|
+
const cached = this.summaryCache.get(blockId);
|
|
62
|
+
if (cached && cached.count === thoughts.length) {
|
|
63
|
+
summary = cached.summary;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
if (thoughts.length > 2) {
|
|
67
|
+
summary = await this.generateSummary(thoughts);
|
|
68
|
+
this.summaryCache.set(blockId, { summary, count: thoughts.length });
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
summary = thoughts.map(t => t.thought).join(' -> ');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Extract topic from first thought or blockId
|
|
75
|
+
const topic = thoughts[0]?.thought.substring(0, 50) || blockId;
|
|
76
|
+
results.push({
|
|
77
|
+
blockId,
|
|
78
|
+
summary,
|
|
79
|
+
topic,
|
|
80
|
+
lastUpdated: maxIndex // Use the array index as the source of truth for recency
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
selectRelevantBlocks(candidates, query) {
|
|
86
|
+
if (candidates.length === 0)
|
|
87
|
+
return [];
|
|
88
|
+
const queryKeywords = this.extractKeywords(query);
|
|
89
|
+
const scored = candidates.map(block => {
|
|
90
|
+
const blockKeywords = this.extractKeywords(block.topic + " " + block.summary);
|
|
91
|
+
const score = this.calculateOverlap(queryKeywords, blockKeywords);
|
|
92
|
+
return { block, score };
|
|
93
|
+
});
|
|
94
|
+
// Sort by score descending, then recency
|
|
95
|
+
scored.sort((a, b) => {
|
|
96
|
+
if (a.score !== b.score)
|
|
97
|
+
return b.score - a.score;
|
|
98
|
+
return b.block.lastUpdated - a.block.lastUpdated;
|
|
99
|
+
});
|
|
100
|
+
// Always include the immediate previous block (for continuity)
|
|
101
|
+
// Find the block with the highest lastUpdated
|
|
102
|
+
const lastBlock = candidates.reduce((prev, current) => (prev.lastUpdated > current.lastUpdated) ? prev : current);
|
|
103
|
+
const selected = new Set();
|
|
104
|
+
selected.add(lastBlock); // Always add last block
|
|
105
|
+
// Add Top N relevant blocks
|
|
106
|
+
for (const item of scored) {
|
|
107
|
+
if (selected.size >= 4)
|
|
108
|
+
break; // Max 4 blocks context
|
|
109
|
+
if (item.score > 0) { // Only if there is some relevance
|
|
110
|
+
selected.add(item.block);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Return sorted by recency (oldest to newest) for logical flow
|
|
114
|
+
return Array.from(selected).sort((a, b) => a.lastUpdated - b.lastUpdated);
|
|
115
|
+
}
|
|
116
|
+
extractKeywords(text) {
|
|
117
|
+
const tokens = text.toLowerCase()
|
|
118
|
+
.replace(/[^a-z0-9\s]/g, '') // Remove symbols
|
|
119
|
+
.split(/\s+/);
|
|
120
|
+
const keywords = new Set();
|
|
121
|
+
for (const t of tokens) {
|
|
122
|
+
if (t.length > 2 && !this.STOP_WORDS.has(t)) {
|
|
123
|
+
keywords.add(t);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return keywords;
|
|
127
|
+
}
|
|
128
|
+
calculateOverlap(setA, setB) {
|
|
129
|
+
let intersection = 0;
|
|
130
|
+
for (const elem of setA) {
|
|
131
|
+
if (setB.has(elem))
|
|
132
|
+
intersection++;
|
|
133
|
+
}
|
|
134
|
+
return intersection;
|
|
135
|
+
}
|
|
136
|
+
async generateSummary(thoughts) {
|
|
137
|
+
// Mock implementation
|
|
138
|
+
const decisions = thoughts.filter(t => t.thoughtType === 'planning' || t.thoughtType === 'selection').length;
|
|
139
|
+
const executions = thoughts.filter(t => t.thoughtType === 'execution').length;
|
|
140
|
+
const topic = thoughts[0]?.thought.substring(0, 30) || 'Unknown';
|
|
141
|
+
return `Topic: "${topic}...". Processed ${thoughts.length} steps (${decisions} plans, ${executions} actions). Outcome: Handed off or completed.`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class RuleManager {
|
|
2
|
+
private rules;
|
|
3
|
+
private storagePath;
|
|
4
|
+
constructor(storagePath?: string);
|
|
5
|
+
private loadRules;
|
|
6
|
+
private saveRules;
|
|
7
|
+
addRule(trigger: string, warning: string): Promise<void>;
|
|
8
|
+
checkRules(execution: string): string[];
|
|
9
|
+
getRulesSummary(): string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
export class RuleManager {
|
|
5
|
+
rules = [];
|
|
6
|
+
storagePath;
|
|
7
|
+
constructor(storagePath = 'learned_rules.json') {
|
|
8
|
+
this.storagePath = path.resolve(storagePath);
|
|
9
|
+
this.loadRules();
|
|
10
|
+
}
|
|
11
|
+
async loadRules() {
|
|
12
|
+
try {
|
|
13
|
+
if (existsSync(this.storagePath)) {
|
|
14
|
+
const data = await fs.readFile(this.storagePath, 'utf-8');
|
|
15
|
+
this.rules = JSON.parse(data);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.error('[RuleManager] Error loading rules:', error);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async saveRules() {
|
|
23
|
+
try {
|
|
24
|
+
await fs.writeFile(this.storagePath, JSON.stringify(this.rules, null, 2), 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error('[RuleManager] Error saving rules:', error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async addRule(trigger, warning) {
|
|
31
|
+
const existing = this.rules.find(r => r.trigger === trigger);
|
|
32
|
+
if (existing) {
|
|
33
|
+
existing.failCount++;
|
|
34
|
+
existing.warning = warning; // Update with latest context
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.rules.push({
|
|
38
|
+
trigger,
|
|
39
|
+
warning,
|
|
40
|
+
createdAt: new Date().toISOString(),
|
|
41
|
+
failCount: 1
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
await this.saveRules();
|
|
45
|
+
}
|
|
46
|
+
checkRules(execution) {
|
|
47
|
+
const warnings = [];
|
|
48
|
+
const lowerExec = execution.toLowerCase();
|
|
49
|
+
for (const rule of this.rules) {
|
|
50
|
+
// Simple keyword check or exact match
|
|
51
|
+
if (lowerExec.includes(rule.trigger.toLowerCase())) {
|
|
52
|
+
warnings.push(`š”ļø IMMUNE SYSTEM WARNING: This action previously failed. Lesson: "${rule.warning}"`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return warnings;
|
|
56
|
+
}
|
|
57
|
+
getRulesSummary() {
|
|
58
|
+
if (this.rules.length === 0)
|
|
59
|
+
return "No rules learned yet.";
|
|
60
|
+
return this.rules.map(r => `- [${r.trigger}] ${r.warning}`).join('\n');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
export function startDashboard(port, historyPath) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const server = http.createServer((req, res) => {
|
|
10
|
+
// Handle CORS
|
|
11
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
12
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET');
|
|
13
|
+
if (req.url === '/') {
|
|
14
|
+
// Serve index.html
|
|
15
|
+
const indexPath = path.join(__dirname, 'index.html');
|
|
16
|
+
fs.readFile(indexPath, (err, data) => {
|
|
17
|
+
if (err) {
|
|
18
|
+
res.writeHead(500);
|
|
19
|
+
res.end('Error loading dashboard');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
23
|
+
res.end(data);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else if (req.url === '/api/history') {
|
|
27
|
+
// Serve thoughts_history.json
|
|
28
|
+
fs.readFile(historyPath, (err, data) => {
|
|
29
|
+
if (err) {
|
|
30
|
+
// Try .tmp if main file is locked/missing (rare fallback)
|
|
31
|
+
fs.readFile(historyPath + '.tmp', (err2, data2) => {
|
|
32
|
+
if (err2) {
|
|
33
|
+
res.writeHead(500);
|
|
34
|
+
res.end(JSON.stringify({ error: 'History not found' }));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
38
|
+
res.end(data2);
|
|
39
|
+
});
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
43
|
+
res.end(data);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
res.writeHead(404);
|
|
48
|
+
res.end('Not found');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
server.on('error', (e) => {
|
|
52
|
+
if (e.code === 'EADDRINUSE') {
|
|
53
|
+
console.error(`[Dashboard] Port ${port} is busy. Trying ${port + 1}...`);
|
|
54
|
+
// Recursive retry
|
|
55
|
+
startDashboard(port + 1, historyPath).then(resolve).catch(reject);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.error('[Dashboard] Server error:', e);
|
|
59
|
+
reject(e);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
server.listen(port, () => {
|
|
63
|
+
console.error(`[Dashboard] š Dashboard running at http://localhost:${port}`);
|
|
64
|
+
resolve(server);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { registerCodingTools } from './tools/coding.js';
|
|
|
19
19
|
import { registerCodeDbTools } from './tools/codestore.js';
|
|
20
20
|
import { registerHumanTools } from './tools/human.js';
|
|
21
21
|
import { registerSportsTools } from './tools/sports.js';
|
|
22
|
+
import { startDashboard } from './dashboard/server.js';
|
|
22
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
24
|
const __dirname = dirname(__filename);
|
|
24
25
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
@@ -27,6 +28,11 @@ const server = new McpServer({
|
|
|
27
28
|
version: pkg.version,
|
|
28
29
|
});
|
|
29
30
|
const thinkingServer = new SequentialThinkingServer(process.env.THOUGHTS_STORAGE_PATH || 'thoughts_history.json', parseInt(process.env.THOUGHT_DELAY_MS || '0', 10));
|
|
31
|
+
// Start Dashboard
|
|
32
|
+
const historyPath = process.env.THOUGHTS_STORAGE_PATH || 'thoughts_history.json';
|
|
33
|
+
startDashboard(3001, historyPath).catch(err => {
|
|
34
|
+
console.error("[Dashboard] Failed to start:", err);
|
|
35
|
+
});
|
|
30
36
|
const knowledgeGraph = new ProjectKnowledgeGraph();
|
|
31
37
|
const memoryGraph = new KnowledgeGraphManager(process.env.MEMORY_GRAPH_PATH || 'knowledge_graph.json');
|
|
32
38
|
const notesManager = new NotesManager(process.env.NOTES_STORAGE_PATH || 'project_notes.json');
|
package/dist/lib.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export declare class SequentialThinkingServer {
|
|
|
38
38
|
private saveMutex;
|
|
39
39
|
private consecutiveStallCount;
|
|
40
40
|
private confidenceScore;
|
|
41
|
+
private contextManager;
|
|
42
|
+
private ruleManager;
|
|
41
43
|
constructor(storagePath?: string, delayMs?: number);
|
|
42
44
|
private loadHistory;
|
|
43
45
|
private attemptRecovery;
|
|
@@ -70,5 +72,6 @@ export declare class SequentialThinkingServer {
|
|
|
70
72
|
thoughtCount: number;
|
|
71
73
|
}[];
|
|
72
74
|
getHistoryLength(): number;
|
|
75
|
+
private learnFromFailures;
|
|
73
76
|
}
|
|
74
77
|
export {};
|
package/dist/lib.js
CHANGED
|
@@ -3,6 +3,8 @@ import * as fs from 'fs/promises';
|
|
|
3
3
|
import { existsSync, readFileSync } from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import { AsyncMutex } from './utils.js';
|
|
6
|
+
import { ContextManager } from './core/ContextManager.js';
|
|
7
|
+
import { RuleManager } from './core/RuleManager.js';
|
|
6
8
|
export class SequentialThinkingServer {
|
|
7
9
|
thoughtHistory = [];
|
|
8
10
|
blocks = [];
|
|
@@ -15,6 +17,8 @@ export class SequentialThinkingServer {
|
|
|
15
17
|
saveMutex = new AsyncMutex();
|
|
16
18
|
consecutiveStallCount = 0; // Track consecutive stalls/loops
|
|
17
19
|
confidenceScore = 100; // Meta-Cognition Score (0-100)
|
|
20
|
+
contextManager = new ContextManager();
|
|
21
|
+
ruleManager = new RuleManager();
|
|
18
22
|
constructor(storagePath = 'thoughts_history.json', delayMs = 0) {
|
|
19
23
|
this.disableThoughtLogging = (process.env.DISABLE_THOUGHT_LOGGING || "").toLowerCase() === "true";
|
|
20
24
|
this.storagePath = path.resolve(storagePath);
|
|
@@ -439,9 +443,9 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('ā') ? wrapp
|
|
|
439
443
|
let feedbackExtension = "";
|
|
440
444
|
// --- š FEATURE 0: Smart Branching Reward (Reset Confidence on Pivot) ---
|
|
441
445
|
if (input.branchFromThought) {
|
|
442
|
-
if (this.confidenceScore <
|
|
443
|
-
this.confidenceScore =
|
|
444
|
-
feedbackExtension += `\nšæ BRANCH DETECTED: Confidence restored to
|
|
446
|
+
if (this.confidenceScore < 75) {
|
|
447
|
+
this.confidenceScore = 75;
|
|
448
|
+
feedbackExtension += `\nšæ BRANCH DETECTED: Confidence restored to 75. Good decision to pivot.`;
|
|
445
449
|
}
|
|
446
450
|
}
|
|
447
451
|
// --- š§ FEATURE 1: Semantic Thought Recall ---
|
|
@@ -521,6 +525,7 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('ā') ? wrapp
|
|
|
521
525
|
const oldBlock = this.blocks.find(b => b.id === this.currentBlockId);
|
|
522
526
|
if (oldBlock) {
|
|
523
527
|
oldBlock.status = 'completed';
|
|
528
|
+
await this.learnFromFailures(oldBlock.id);
|
|
524
529
|
// Auto-Summary Trigger
|
|
525
530
|
if (oldBlock.thoughts.length > 5) {
|
|
526
531
|
warnings.push(`š CONTEXT MANAGEMENT: Previous block '${oldBlock.topic.substring(0, 30)}...' was long. Please use 'summarize_history' to compress it.`);
|
|
@@ -558,10 +563,16 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('ā') ? wrapp
|
|
|
558
563
|
}
|
|
559
564
|
}
|
|
560
565
|
// Rule 2: Repeating Action Detection
|
|
561
|
-
if (input.thoughtType === 'execution'
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
566
|
+
if (input.thoughtType === 'execution') {
|
|
567
|
+
// Check Immune System Rules
|
|
568
|
+
const ruleWarnings = this.ruleManager.checkRules(input.thought);
|
|
569
|
+
ruleWarnings.forEach(w => warnings.push(w));
|
|
570
|
+
if (ruleWarnings.length > 0)
|
|
571
|
+
this.confidenceScore -= 10;
|
|
572
|
+
if (recentInBlock.some(t => t.thoughtType === 'execution' && t.thought === input.thought)) {
|
|
573
|
+
warnings.push(`š LOOP DETECTED: You are attempting to execute the exact same action again. You MUST change your strategy or create a branch with a different approach.`);
|
|
574
|
+
isStallingOrLooping = true;
|
|
575
|
+
}
|
|
565
576
|
}
|
|
566
577
|
// Rule 3: Missing Observation
|
|
567
578
|
if (lastThought &&
|
|
@@ -644,6 +655,7 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('ā') ? wrapp
|
|
|
644
655
|
.filter(t => t.thoughtType === 'execution')
|
|
645
656
|
.map(t => t.thought);
|
|
646
657
|
const activeBlock = this.blocks.find(b => b.id === input.blockId);
|
|
658
|
+
await this.learnFromFailures(input.blockId || "");
|
|
647
659
|
await this.saveSolution(activeBlock?.topic || 'Untitled', input.thought, solutionSteps);
|
|
648
660
|
}
|
|
649
661
|
}
|
|
@@ -660,7 +672,10 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('ā') ? wrapp
|
|
|
660
672
|
// --- š Update Confidence Score ---
|
|
661
673
|
if (warnings.length > 0)
|
|
662
674
|
this.confidenceScore -= (5 * warnings.length);
|
|
663
|
-
if (input.thoughtType === '
|
|
675
|
+
if (input.thoughtType === 'reflexion') {
|
|
676
|
+
this.confidenceScore = Math.min(100, this.confidenceScore + 10);
|
|
677
|
+
}
|
|
678
|
+
else if (input.thoughtType === 'observation') {
|
|
664
679
|
const isError = input.toolResult?.toLowerCase().includes('error');
|
|
665
680
|
if (isError)
|
|
666
681
|
this.confidenceScore -= 10;
|
|
@@ -681,6 +696,7 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('ā') ? wrapp
|
|
|
681
696
|
// D. Generate Contextual Output
|
|
682
697
|
const currentBlock = this.blocks.find(b => b.id === input.blockId);
|
|
683
698
|
const mermaid = this.generateMermaid();
|
|
699
|
+
const optimizedContext = await this.contextManager.getOptimizedContext(this.thoughtHistory, input.blockId || null);
|
|
684
700
|
if (feedbackExtension)
|
|
685
701
|
warnings.push(feedbackExtension);
|
|
686
702
|
return {
|
|
@@ -694,6 +710,7 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('ā') ? wrapp
|
|
|
694
710
|
blockContext: currentBlock
|
|
695
711
|
? `Block '${currentBlock.topic.substring(0, 30)}' has ${currentBlock.thoughts.length} thoughts.`
|
|
696
712
|
: 'No active block',
|
|
713
|
+
optimizedContext,
|
|
697
714
|
branches: Object.keys(this.branches),
|
|
698
715
|
thoughtHistoryLength: this.thoughtHistory.length,
|
|
699
716
|
feedback: warnings.length > 0 ? warnings : "Flow Healthy ā
",
|
|
@@ -801,4 +818,18 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('ā') ? wrapp
|
|
|
801
818
|
getHistoryLength() {
|
|
802
819
|
return this.thoughtHistory.length;
|
|
803
820
|
}
|
|
821
|
+
async learnFromFailures(blockId) {
|
|
822
|
+
const blockThoughts = this.thoughtHistory.filter(t => t.blockId === blockId);
|
|
823
|
+
for (let i = 0; i < blockThoughts.length - 1; i++) {
|
|
824
|
+
const current = blockThoughts[i];
|
|
825
|
+
const next = blockThoughts[i + 1];
|
|
826
|
+
if (current.thoughtType === 'execution' && next.thoughtType === 'observation') {
|
|
827
|
+
const res = next.toolResult?.toLowerCase() || "";
|
|
828
|
+
// Failure keywords
|
|
829
|
+
if (res.includes("error") || res.includes("fail") || res.includes("exception") || res.includes("enoent")) {
|
|
830
|
+
await this.ruleManager.addRule(current.thought, res.substring(0, 100).replace(/\n/g, ' '));
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
804
835
|
}
|