@gotza02/sequential-thinking 2026.3.11 → 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 +5 -2
- package/dist/core/ContextManager.js +105 -24
- 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 +2 -0
- package/dist/lib.js +32 -5
- package/package.json +1 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { ThoughtData } from '../lib.js';
|
|
2
2
|
export declare class ContextManager {
|
|
3
3
|
private summaryCache;
|
|
4
|
+
private readonly STOP_WORDS;
|
|
4
5
|
constructor();
|
|
5
6
|
getOptimizedContext(history: ThoughtData[], currentBlockId: string | null): Promise<string>;
|
|
6
|
-
private
|
|
7
|
-
private
|
|
7
|
+
private getBlockSummaries;
|
|
8
|
+
private selectRelevantBlocks;
|
|
9
|
+
private extractKeywords;
|
|
10
|
+
private calculateOverlap;
|
|
8
11
|
private generateSummary;
|
|
9
12
|
}
|
|
@@ -1,56 +1,137 @@
|
|
|
1
1
|
export class ContextManager {
|
|
2
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
|
+
]);
|
|
3
11
|
constructor() { }
|
|
4
12
|
async getOptimizedContext(history, currentBlockId) {
|
|
5
|
-
// If no current block, treat everything as old or just show all?
|
|
6
|
-
// If null, we might be in setup mode. Let's assume empty current block.
|
|
7
13
|
const safeCurrentBlockId = currentBlockId || '';
|
|
8
|
-
const
|
|
14
|
+
const oldBlocksThoughts = history.filter(t => t.blockId !== safeCurrentBlockId);
|
|
9
15
|
const activeBlock = history.filter(t => t.blockId === safeCurrentBlockId);
|
|
10
|
-
|
|
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
|
+
}
|
|
11
33
|
// Format active block detailed
|
|
12
34
|
const activeContext = activeBlock.map(t => `[${t.thoughtType?.toUpperCase() || 'INFO'}] #${t.thoughtNumber}: ${t.thought}`).join('\n');
|
|
13
35
|
return `
|
|
14
|
-
=== PROJECT HISTORY (
|
|
15
|
-
${
|
|
36
|
+
=== PROJECT HISTORY (SMART CONTEXT) ===
|
|
37
|
+
${summariesText}
|
|
16
38
|
|
|
17
39
|
=== CURRENT FOCUS (DETAILED: ${safeCurrentBlockId || 'GLOBAL'}) ===
|
|
18
40
|
${activeContext}
|
|
19
41
|
`;
|
|
20
42
|
}
|
|
21
|
-
async
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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) {
|
|
26
57
|
if (!blockId)
|
|
27
58
|
continue;
|
|
59
|
+
const { thoughts, maxIndex } = data;
|
|
60
|
+
let summary = "";
|
|
28
61
|
const cached = this.summaryCache.get(blockId);
|
|
29
62
|
if (cached && cached.count === thoughts.length) {
|
|
30
|
-
|
|
63
|
+
summary = cached.summary;
|
|
31
64
|
}
|
|
32
65
|
else {
|
|
33
66
|
if (thoughts.length > 2) {
|
|
34
|
-
|
|
67
|
+
summary = await this.generateSummary(thoughts);
|
|
35
68
|
this.summaryCache.set(blockId, { summary, count: thoughts.length });
|
|
36
|
-
result += `[Block: ${blockId}] Summary: ${summary}\n`;
|
|
37
69
|
}
|
|
38
70
|
else {
|
|
39
|
-
|
|
71
|
+
summary = thoughts.map(t => t.thought).join(' -> ');
|
|
40
72
|
}
|
|
41
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
|
+
});
|
|
42
82
|
}
|
|
43
|
-
return
|
|
83
|
+
return results;
|
|
44
84
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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;
|
|
52
99
|
});
|
|
53
|
-
|
|
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;
|
|
54
135
|
}
|
|
55
136
|
async generateSummary(thoughts) {
|
|
56
137
|
// Mock implementation
|
|
@@ -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
|
@@ -39,6 +39,7 @@ export declare class SequentialThinkingServer {
|
|
|
39
39
|
private consecutiveStallCount;
|
|
40
40
|
private confidenceScore;
|
|
41
41
|
private contextManager;
|
|
42
|
+
private ruleManager;
|
|
42
43
|
constructor(storagePath?: string, delayMs?: number);
|
|
43
44
|
private loadHistory;
|
|
44
45
|
private attemptRecovery;
|
|
@@ -71,5 +72,6 @@ export declare class SequentialThinkingServer {
|
|
|
71
72
|
thoughtCount: number;
|
|
72
73
|
}[];
|
|
73
74
|
getHistoryLength(): number;
|
|
75
|
+
private learnFromFailures;
|
|
74
76
|
}
|
|
75
77
|
export {};
|
package/dist/lib.js
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import { AsyncMutex } from './utils.js';
|
|
6
6
|
import { ContextManager } from './core/ContextManager.js';
|
|
7
|
+
import { RuleManager } from './core/RuleManager.js';
|
|
7
8
|
export class SequentialThinkingServer {
|
|
8
9
|
thoughtHistory = [];
|
|
9
10
|
blocks = [];
|
|
@@ -17,6 +18,7 @@ export class SequentialThinkingServer {
|
|
|
17
18
|
consecutiveStallCount = 0; // Track consecutive stalls/loops
|
|
18
19
|
confidenceScore = 100; // Meta-Cognition Score (0-100)
|
|
19
20
|
contextManager = new ContextManager();
|
|
21
|
+
ruleManager = new RuleManager();
|
|
20
22
|
constructor(storagePath = 'thoughts_history.json', delayMs = 0) {
|
|
21
23
|
this.disableThoughtLogging = (process.env.DISABLE_THOUGHT_LOGGING || "").toLowerCase() === "true";
|
|
22
24
|
this.storagePath = path.resolve(storagePath);
|
|
@@ -523,6 +525,7 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('│') ? wrapp
|
|
|
523
525
|
const oldBlock = this.blocks.find(b => b.id === this.currentBlockId);
|
|
524
526
|
if (oldBlock) {
|
|
525
527
|
oldBlock.status = 'completed';
|
|
528
|
+
await this.learnFromFailures(oldBlock.id);
|
|
526
529
|
// Auto-Summary Trigger
|
|
527
530
|
if (oldBlock.thoughts.length > 5) {
|
|
528
531
|
warnings.push(`📜 CONTEXT MANAGEMENT: Previous block '${oldBlock.topic.substring(0, 30)}...' was long. Please use 'summarize_history' to compress it.`);
|
|
@@ -560,10 +563,16 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('│') ? wrapp
|
|
|
560
563
|
}
|
|
561
564
|
}
|
|
562
565
|
// Rule 2: Repeating Action Detection
|
|
563
|
-
if (input.thoughtType === 'execution'
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
+
}
|
|
567
576
|
}
|
|
568
577
|
// Rule 3: Missing Observation
|
|
569
578
|
if (lastThought &&
|
|
@@ -646,6 +655,7 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('│') ? wrapp
|
|
|
646
655
|
.filter(t => t.thoughtType === 'execution')
|
|
647
656
|
.map(t => t.thought);
|
|
648
657
|
const activeBlock = this.blocks.find(b => b.id === input.blockId);
|
|
658
|
+
await this.learnFromFailures(input.blockId || "");
|
|
649
659
|
await this.saveSolution(activeBlock?.topic || 'Untitled', input.thought, solutionSteps);
|
|
650
660
|
}
|
|
651
661
|
}
|
|
@@ -662,7 +672,10 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('│') ? wrapp
|
|
|
662
672
|
// --- 📊 Update Confidence Score ---
|
|
663
673
|
if (warnings.length > 0)
|
|
664
674
|
this.confidenceScore -= (5 * warnings.length);
|
|
665
|
-
if (input.thoughtType === '
|
|
675
|
+
if (input.thoughtType === 'reflexion') {
|
|
676
|
+
this.confidenceScore = Math.min(100, this.confidenceScore + 10);
|
|
677
|
+
}
|
|
678
|
+
else if (input.thoughtType === 'observation') {
|
|
666
679
|
const isError = input.toolResult?.toLowerCase().includes('error');
|
|
667
680
|
if (isError)
|
|
668
681
|
this.confidenceScore -= 10;
|
|
@@ -805,4 +818,18 @@ ${typeof wrappedThought === 'string' && wrappedThought.startsWith('│') ? wrapp
|
|
|
805
818
|
getHistoryLength() {
|
|
806
819
|
return this.thoughtHistory.length;
|
|
807
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
|
+
}
|
|
808
835
|
}
|