@kernel.chat/kbot 3.23.0 → 3.26.1
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/README.md +33 -22
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +3 -1
- package/dist/agent.js.map +1 -1
- package/dist/agents/trader.d.ts +32 -0
- package/dist/agents/trader.d.ts.map +1 -0
- package/dist/agents/trader.js +190 -0
- package/dist/agents/trader.js.map +1 -0
- package/dist/cli.js +219 -15
- package/dist/cli.js.map +1 -1
- package/dist/context.d.ts +4 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +28 -1
- package/dist/context.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +71 -1
- package/dist/doctor.js.map +1 -1
- package/dist/inference.d.ts +9 -0
- package/dist/inference.d.ts.map +1 -1
- package/dist/inference.js +38 -5
- package/dist/inference.js.map +1 -1
- package/dist/introspection.d.ts +17 -0
- package/dist/introspection.d.ts.map +1 -0
- package/dist/introspection.js +490 -0
- package/dist/introspection.js.map +1 -0
- package/dist/learned-router.d.ts.map +1 -1
- package/dist/learned-router.js +3 -0
- package/dist/learned-router.js.map +1 -1
- package/dist/machine.d.ts +85 -0
- package/dist/machine.d.ts.map +1 -0
- package/dist/machine.js +538 -0
- package/dist/machine.js.map +1 -0
- package/dist/matrix.d.ts.map +1 -1
- package/dist/matrix.js +11 -0
- package/dist/matrix.js.map +1 -1
- package/dist/provider-fallback.d.ts +6 -0
- package/dist/provider-fallback.d.ts.map +1 -1
- package/dist/provider-fallback.js +29 -0
- package/dist/provider-fallback.js.map +1 -1
- package/dist/synthesis-engine.d.ts +175 -0
- package/dist/synthesis-engine.d.ts.map +1 -0
- package/dist/synthesis-engine.js +783 -0
- package/dist/synthesis-engine.js.map +1 -0
- package/dist/tool-pipeline.d.ts +7 -1
- package/dist/tool-pipeline.d.ts.map +1 -1
- package/dist/tool-pipeline.js +39 -1
- package/dist/tool-pipeline.js.map +1 -1
- package/dist/tools/finance.d.ts +2 -0
- package/dist/tools/finance.d.ts.map +1 -0
- package/dist/tools/finance.js +1116 -0
- package/dist/tools/finance.js.map +1 -0
- package/dist/tools/finance.test.d.ts +2 -0
- package/dist/tools/finance.test.d.ts.map +1 -0
- package/dist/tools/finance.test.js +245 -0
- package/dist/tools/finance.test.js.map +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +5 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/machine-tools.d.ts +2 -0
- package/dist/tools/machine-tools.d.ts.map +1 -0
- package/dist/tools/machine-tools.js +690 -0
- package/dist/tools/machine-tools.js.map +1 -0
- package/dist/tools/sentiment.d.ts +2 -0
- package/dist/tools/sentiment.d.ts.map +1 -0
- package/dist/tools/sentiment.js +513 -0
- package/dist/tools/sentiment.js.map +1 -0
- package/dist/tools/stocks.d.ts +2 -0
- package/dist/tools/stocks.d.ts.map +1 -0
- package/dist/tools/stocks.js +345 -0
- package/dist/tools/stocks.js.map +1 -0
- package/dist/tools/stocks.test.d.ts +2 -0
- package/dist/tools/stocks.test.d.ts.map +1 -0
- package/dist/tools/stocks.test.js +82 -0
- package/dist/tools/stocks.test.js.map +1 -0
- package/dist/tools/wallet.d.ts +2 -0
- package/dist/tools/wallet.d.ts.map +1 -0
- package/dist/tools/wallet.js +698 -0
- package/dist/tools/wallet.js.map +1 -0
- package/dist/tools/wallet.test.d.ts +2 -0
- package/dist/tools/wallet.test.d.ts.map +1 -0
- package/dist/tools/wallet.test.js +205 -0
- package/dist/tools/wallet.test.js.map +1 -0
- package/dist/ui.js +1 -1
- package/dist/ui.js.map +1 -1
- package/package.json +94 -42
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
// kbot Synthesis Engine — Closed-Loop Intelligence Compounding
|
|
2
|
+
//
|
|
3
|
+
// The bridge between SELF-DISCOVERY (learning, reflections, skill ratings)
|
|
4
|
+
// and UNIVERSE-DISCOVERY (tools, agents, papers, opportunities, engagement).
|
|
5
|
+
//
|
|
6
|
+
// Eight synthesis operations:
|
|
7
|
+
// 1. consumeDiscoveredTools — evaluate discovered tools against failure patterns
|
|
8
|
+
// 2. instantiateProposedAgents — trial proposed agents against skill gaps
|
|
9
|
+
// 3. extractPaperInsights — pull implementable patterns from academic papers
|
|
10
|
+
// 4. buildActiveCorrections — corrections + reflections → prompt injection
|
|
11
|
+
// 5. closeReflectionLoop — reflections → skill rating adjustments
|
|
12
|
+
// 6. crossPollinatePatterns — transfer patterns across projects
|
|
13
|
+
// 7. buildSkillMap — Bayesian ratings → human-readable map
|
|
14
|
+
// 8. feedEngagementBack — engagement outcomes → topic weights
|
|
15
|
+
//
|
|
16
|
+
// All operations are heuristic — no LLM calls. Fast and free.
|
|
17
|
+
// Storage: ~/.kbot/memory/synthesis-engine.json
|
|
18
|
+
// ~/.kbot/memory/active-corrections.json
|
|
19
|
+
// ~/.kbot/memory/skill-map.json
|
|
20
|
+
// .kbot-discovery/topic-weights.json (if discovery exists)
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
24
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
25
|
+
// Paths
|
|
26
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
27
|
+
const MEMORY_DIR = join(homedir(), '.kbot', 'memory');
|
|
28
|
+
const STATE_FILE = join(MEMORY_DIR, 'synthesis-engine.json');
|
|
29
|
+
const CORRECTIONS_FILE = join(MEMORY_DIR, 'active-corrections.json');
|
|
30
|
+
const SKILL_MAP_FILE = join(MEMORY_DIR, 'skill-map.json');
|
|
31
|
+
// Learning stores
|
|
32
|
+
const PATTERNS_FILE = join(MEMORY_DIR, 'patterns.json');
|
|
33
|
+
const REFLECTIONS_FILE = join(MEMORY_DIR, 'reflections.json');
|
|
34
|
+
const SKILL_RATINGS_FILE = join(MEMORY_DIR, 'skill-ratings.json');
|
|
35
|
+
const CORRECTIONS_LEARNING_FILE = join(MEMORY_DIR, 'corrections.json');
|
|
36
|
+
const PROJECTS_FILE = join(MEMORY_DIR, 'projects.json');
|
|
37
|
+
const ROUTING_HISTORY_FILE = join(MEMORY_DIR, 'routing-history.json');
|
|
38
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
39
|
+
// Helpers
|
|
40
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
41
|
+
function ensureDir(dir) {
|
|
42
|
+
if (!existsSync(dir))
|
|
43
|
+
mkdirSync(dir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
function loadJSON(path, fallback) {
|
|
46
|
+
if (!existsSync(path))
|
|
47
|
+
return fallback;
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return fallback;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function saveJSON(path, data) {
|
|
56
|
+
ensureDir(join(path, '..'));
|
|
57
|
+
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
58
|
+
}
|
|
59
|
+
function loadState() {
|
|
60
|
+
return loadJSON(STATE_FILE, {
|
|
61
|
+
toolAdoptions: [],
|
|
62
|
+
agentTrials: [],
|
|
63
|
+
paperInsights: [],
|
|
64
|
+
skillMap: [],
|
|
65
|
+
topicWeights: [],
|
|
66
|
+
crossPollinatedCount: 0,
|
|
67
|
+
lastCycleAt: '',
|
|
68
|
+
totalCycles: 0,
|
|
69
|
+
stats: {
|
|
70
|
+
toolsEvaluated: 0, toolsAdopted: 0, toolsRejected: 0,
|
|
71
|
+
agentsTrialed: 0, agentsKept: 0, agentsDissolved: 0,
|
|
72
|
+
papersAnalyzed: 0, patternsImplemented: 0,
|
|
73
|
+
correctionsActive: 0, reflectionsClosed: 0,
|
|
74
|
+
patternsTransferred: 0, engagementsFedBack: 0,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function saveState(state) {
|
|
79
|
+
ensureDir(MEMORY_DIR);
|
|
80
|
+
saveJSON(STATE_FILE, state);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Evaluate discovered tools against failure patterns in reflections.
|
|
84
|
+
* If a discovered tool solves a problem that caused repeated failures,
|
|
85
|
+
* mark it for adoption. Otherwise, mark as evaluated and move on.
|
|
86
|
+
*/
|
|
87
|
+
export function consumeDiscoveredTools(discoveryDir) {
|
|
88
|
+
const state = loadState();
|
|
89
|
+
const outreachFile = join(discoveryDir, 'outreach', 'latest.json');
|
|
90
|
+
if (!existsSync(outreachFile))
|
|
91
|
+
return state.toolAdoptions;
|
|
92
|
+
const outreach = loadJSON(outreachFile, { projects: [] });
|
|
93
|
+
const projects = outreach.projects || [];
|
|
94
|
+
if (projects.length === 0)
|
|
95
|
+
return state.toolAdoptions;
|
|
96
|
+
// Load reflections to find failure patterns
|
|
97
|
+
const reflections = loadJSON(REFLECTIONS_FILE, []);
|
|
98
|
+
// Extract failure keywords from reflections
|
|
99
|
+
const failureKeywords = new Set();
|
|
100
|
+
for (const r of reflections) {
|
|
101
|
+
const words = (r.lesson + ' ' + r.taskMessage).toLowerCase()
|
|
102
|
+
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
103
|
+
.split(/\s+/)
|
|
104
|
+
.filter(w => w.length > 3);
|
|
105
|
+
for (const w of words)
|
|
106
|
+
failureKeywords.add(w);
|
|
107
|
+
}
|
|
108
|
+
// Already evaluated tools (by URL)
|
|
109
|
+
const evaluatedUrls = new Set(state.toolAdoptions.map(t => t.url));
|
|
110
|
+
const newAdoptions = [];
|
|
111
|
+
for (const project of projects) {
|
|
112
|
+
if (evaluatedUrls.has(project.url))
|
|
113
|
+
continue;
|
|
114
|
+
// Score: how many failure keywords appear in the project description?
|
|
115
|
+
const desc = project.description.toLowerCase();
|
|
116
|
+
const descWords = desc.replace(/[^a-z0-9\s-]/g, ' ').split(/\s+/);
|
|
117
|
+
const matchCount = descWords.filter(w => failureKeywords.has(w)).length;
|
|
118
|
+
const matchedWords = descWords.filter(w => failureKeywords.has(w));
|
|
119
|
+
// Also check: does it solve a capability gap? (agent, tool, search, fetch, etc.)
|
|
120
|
+
const capabilityTerms = ['agent', 'tool', 'cli', 'search', 'fetch', 'automation', 'mcp', 'plugin'];
|
|
121
|
+
const capScore = capabilityTerms.filter(t => desc.includes(t)).length;
|
|
122
|
+
const totalScore = matchCount + capScore;
|
|
123
|
+
const adoption = {
|
|
124
|
+
name: project.name,
|
|
125
|
+
url: project.url,
|
|
126
|
+
stars: project.stars,
|
|
127
|
+
reason: totalScore >= 3
|
|
128
|
+
? `Matches ${matchCount} failure keywords [${matchedWords.slice(0, 3).join(', ')}] + ${capScore} capability terms`
|
|
129
|
+
: totalScore >= 1
|
|
130
|
+
? `Low relevance: ${matchCount} failure keywords, ${capScore} capability terms`
|
|
131
|
+
: 'No match to current failure patterns',
|
|
132
|
+
status: totalScore >= 3 && project.stars >= 50 ? 'adopted' : 'rejected',
|
|
133
|
+
matchedReflection: matchCount > 0 ? matchedWords.slice(0, 3).join(', ') : undefined,
|
|
134
|
+
evaluatedAt: new Date().toISOString(),
|
|
135
|
+
};
|
|
136
|
+
newAdoptions.push(adoption);
|
|
137
|
+
}
|
|
138
|
+
// Merge and cap at 100
|
|
139
|
+
state.toolAdoptions = [...state.toolAdoptions, ...newAdoptions].slice(-100);
|
|
140
|
+
state.stats.toolsEvaluated += newAdoptions.length;
|
|
141
|
+
state.stats.toolsAdopted += newAdoptions.filter(t => t.status === 'adopted').length;
|
|
142
|
+
state.stats.toolsRejected += newAdoptions.filter(t => t.status === 'rejected').length;
|
|
143
|
+
saveState(state);
|
|
144
|
+
return newAdoptions;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Evaluate proposed agents against Bayesian skill rating gaps.
|
|
148
|
+
* If an agent targets a category with high sigma (uncertainty),
|
|
149
|
+
* start a trial. After enough tasks, keep or dissolve.
|
|
150
|
+
*/
|
|
151
|
+
export function instantiateProposedAgents(discoveryDir) {
|
|
152
|
+
const state = loadState();
|
|
153
|
+
const agentsFile = join(discoveryDir, 'proposed-agents.json');
|
|
154
|
+
// Also check outreach for proposed agents
|
|
155
|
+
let proposed = [];
|
|
156
|
+
if (existsSync(agentsFile)) {
|
|
157
|
+
proposed = loadJSON(agentsFile, []);
|
|
158
|
+
}
|
|
159
|
+
if (proposed.length === 0)
|
|
160
|
+
return state.agentTrials;
|
|
161
|
+
// Load skill ratings to find gaps
|
|
162
|
+
const ratings = loadJSON(SKILL_RATINGS_FILE, {});
|
|
163
|
+
// Find categories with high sigma (uncertainty) across all agents
|
|
164
|
+
const categories = ['coding', 'debugging', 'refactoring', 'research', 'analysis',
|
|
165
|
+
'writing', 'devops', 'security', 'design', 'general', 'data', 'communication'];
|
|
166
|
+
const categoryGaps = {}; // category → avg sigma
|
|
167
|
+
for (const cat of categories) {
|
|
168
|
+
let totalSigma = 0;
|
|
169
|
+
let count = 0;
|
|
170
|
+
for (const agentRatings of Object.values(ratings)) {
|
|
171
|
+
if (agentRatings[cat]) {
|
|
172
|
+
totalSigma += agentRatings[cat].sigma;
|
|
173
|
+
count++;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// If no agent has been tested in this category, sigma is max (8.33)
|
|
177
|
+
categoryGaps[cat] = count > 0 ? totalSigma / count : 8.33;
|
|
178
|
+
}
|
|
179
|
+
// Already trialing agents (by name)
|
|
180
|
+
const trialingNames = new Set(state.agentTrials.map(t => t.name));
|
|
181
|
+
const newTrials = [];
|
|
182
|
+
for (const agent of proposed) {
|
|
183
|
+
if (trialingNames.has(agent.name))
|
|
184
|
+
continue;
|
|
185
|
+
// Find the best matching category for this agent
|
|
186
|
+
const nameWords = agent.name.toLowerCase().split(/[\s-_]+/);
|
|
187
|
+
const promptWords = (agent.systemPrompt || agent.reasoning || '').toLowerCase()
|
|
188
|
+
.replace(/[^a-z0-9\s]/g, ' ').split(/\s+/);
|
|
189
|
+
const allWords = [...nameWords, ...promptWords];
|
|
190
|
+
let bestCategory = 'general';
|
|
191
|
+
let bestScore = 0;
|
|
192
|
+
for (const cat of categories) {
|
|
193
|
+
const catWords = cat.split(/[\s-_]+/);
|
|
194
|
+
const matches = catWords.filter(w => allWords.includes(w)).length;
|
|
195
|
+
if (matches > bestScore) {
|
|
196
|
+
bestScore = matches;
|
|
197
|
+
bestCategory = cat;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// Only trial if the target category has high uncertainty
|
|
201
|
+
const gap = categoryGaps[bestCategory] || 8.33;
|
|
202
|
+
if (gap < 4)
|
|
203
|
+
continue; // Category already well-covered
|
|
204
|
+
const trial = {
|
|
205
|
+
name: agent.name,
|
|
206
|
+
systemPrompt: (agent.systemPrompt || '').slice(0, 500),
|
|
207
|
+
targetCategory: bestCategory,
|
|
208
|
+
status: 'trialing',
|
|
209
|
+
taskCount: 0,
|
|
210
|
+
mu: 25,
|
|
211
|
+
sigma: 8.33,
|
|
212
|
+
createdAt: new Date().toISOString(),
|
|
213
|
+
};
|
|
214
|
+
newTrials.push(trial);
|
|
215
|
+
}
|
|
216
|
+
state.agentTrials = [...state.agentTrials, ...newTrials].slice(-50);
|
|
217
|
+
state.stats.agentsTrialed += newTrials.length;
|
|
218
|
+
saveState(state);
|
|
219
|
+
return newTrials;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Match academic paper techniques against existing tool patterns.
|
|
223
|
+
* If a paper describes an optimization that maps to a known pattern,
|
|
224
|
+
* propose it as an improvement.
|
|
225
|
+
*/
|
|
226
|
+
export function extractPaperInsights(discoveryDir) {
|
|
227
|
+
const state = loadState();
|
|
228
|
+
const outreachFile = join(discoveryDir, 'outreach', 'latest.json');
|
|
229
|
+
if (!existsSync(outreachFile))
|
|
230
|
+
return state.paperInsights;
|
|
231
|
+
const outreach = loadJSON(outreachFile, {});
|
|
232
|
+
const papers = [];
|
|
233
|
+
if (outreach.latestPaper)
|
|
234
|
+
papers.push(outreach.latestPaper);
|
|
235
|
+
if (outreach.papers)
|
|
236
|
+
papers.push(...outreach.papers);
|
|
237
|
+
if (papers.length === 0)
|
|
238
|
+
return state.paperInsights;
|
|
239
|
+
const patterns = loadJSON(PATTERNS_FILE, []);
|
|
240
|
+
// Extract technique keywords from papers
|
|
241
|
+
const analyzedTitles = new Set(state.paperInsights.map(p => p.title));
|
|
242
|
+
const newInsights = [];
|
|
243
|
+
// Technique detection patterns
|
|
244
|
+
const techniquePatterns = [
|
|
245
|
+
{ regex: /seek|search.*guided|tool.*guided/i, technique: 'Guided seeking instead of exhaustive parsing', applicableTo: 'read_file + grep patterns' },
|
|
246
|
+
{ regex: /retrieval.*augment|rag/i, technique: 'Retrieval-augmented generation', applicableTo: 'research and fact-checking patterns' },
|
|
247
|
+
{ regex: /plan.*then.*act|planning.*execution/i, technique: 'Plan-then-act decomposition', applicableTo: 'multi-step task patterns' },
|
|
248
|
+
{ regex: /self.*correct|self.*refin/i, technique: 'Self-correction loop', applicableTo: 'error-correction patterns' },
|
|
249
|
+
{ regex: /tool.*use|tool.*call|function.*call/i, technique: 'Improved tool selection', applicableTo: 'tool routing patterns' },
|
|
250
|
+
{ regex: /multi.*agent|agent.*collab/i, technique: 'Multi-agent collaboration', applicableTo: 'swarm and routing patterns' },
|
|
251
|
+
{ regex: /memory|context.*window|long.*context/i, technique: 'Memory optimization', applicableTo: 'context management patterns' },
|
|
252
|
+
{ regex: /benchmark|eval/i, technique: 'Evaluation methodology', applicableTo: 'self-eval and skill rating patterns' },
|
|
253
|
+
{ regex: /efficient|lightweight|compress/i, technique: 'Efficiency optimization', applicableTo: 'token reduction patterns' },
|
|
254
|
+
{ regex: /autonomous|self.*improv/i, technique: 'Autonomous improvement', applicableTo: 'evolution and autopoiesis patterns' },
|
|
255
|
+
];
|
|
256
|
+
for (const paper of papers) {
|
|
257
|
+
if (analyzedTitles.has(paper.title))
|
|
258
|
+
continue;
|
|
259
|
+
const text = `${paper.title} ${paper.summary || ''}`;
|
|
260
|
+
for (const tp of techniquePatterns) {
|
|
261
|
+
if (tp.regex.test(text)) {
|
|
262
|
+
// Check if we have patterns in the applicable area
|
|
263
|
+
const applicablePatterns = patterns.filter(p => tp.applicableTo.split(/\s+/).some(w => p.toolSequence.some(t => t.includes(w)) || p.intent.includes(w)));
|
|
264
|
+
newInsights.push({
|
|
265
|
+
title: paper.title,
|
|
266
|
+
url: paper.url || '',
|
|
267
|
+
technique: tp.technique,
|
|
268
|
+
applicableTo: tp.applicableTo + (applicablePatterns.length > 0
|
|
269
|
+
? ` (${applicablePatterns.length} matching patterns found)`
|
|
270
|
+
: ' (no matching patterns yet)'),
|
|
271
|
+
status: applicablePatterns.length > 0 ? 'proposed' : 'rejected',
|
|
272
|
+
extractedAt: new Date().toISOString(),
|
|
273
|
+
});
|
|
274
|
+
break; // One insight per paper
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
state.paperInsights = [...state.paperInsights, ...newInsights].slice(-100);
|
|
279
|
+
state.stats.papersAnalyzed += newInsights.length;
|
|
280
|
+
saveState(state);
|
|
281
|
+
return newInsights;
|
|
282
|
+
}
|
|
283
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
284
|
+
// 4. BUILD ACTIVE CORRECTIONS
|
|
285
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
286
|
+
/**
|
|
287
|
+
* Extract actionable corrections from:
|
|
288
|
+
* - Explicit corrections in corrections.json
|
|
289
|
+
* - Implicit corrections from reflection failure patterns
|
|
290
|
+
* - Pattern failures (low success rate patterns)
|
|
291
|
+
*
|
|
292
|
+
* Produces active-corrections.json for prompt injection.
|
|
293
|
+
*/
|
|
294
|
+
export function buildActiveCorrections() {
|
|
295
|
+
const state = loadState();
|
|
296
|
+
const corrections = loadJSON(CORRECTIONS_LEARNING_FILE, []);
|
|
297
|
+
const active = [];
|
|
298
|
+
for (const c of corrections) {
|
|
299
|
+
if (c.rule && c.occurrences >= 1) {
|
|
300
|
+
active.push({
|
|
301
|
+
rule: c.rule,
|
|
302
|
+
source: 'explicit',
|
|
303
|
+
severity: c.occurrences >= 3 ? 'high' : c.occurrences >= 2 ? 'medium' : 'low',
|
|
304
|
+
occurrences: c.occurrences,
|
|
305
|
+
extractedAt: new Date().toISOString(),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const reflections = loadJSON(REFLECTIONS_FILE, []);
|
|
310
|
+
// Group reflection lessons by theme
|
|
311
|
+
const lessonThemes = {};
|
|
312
|
+
for (const r of reflections) {
|
|
313
|
+
if (!r.lesson)
|
|
314
|
+
continue;
|
|
315
|
+
// Normalize: extract the core rule from the lesson
|
|
316
|
+
const normalized = r.lesson.toLowerCase()
|
|
317
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
318
|
+
.replace(/\s+/g, ' ')
|
|
319
|
+
.trim()
|
|
320
|
+
.slice(0, 100);
|
|
321
|
+
lessonThemes[normalized] = (lessonThemes[normalized] || 0) + 1;
|
|
322
|
+
}
|
|
323
|
+
// Top recurring themes become corrections
|
|
324
|
+
const sortedThemes = Object.entries(lessonThemes)
|
|
325
|
+
.sort((a, b) => b[1] - a[1])
|
|
326
|
+
.slice(0, 10);
|
|
327
|
+
for (const [theme, count] of sortedThemes) {
|
|
328
|
+
if (count >= 2) {
|
|
329
|
+
active.push({
|
|
330
|
+
rule: theme,
|
|
331
|
+
source: 'reflection',
|
|
332
|
+
severity: count >= 5 ? 'high' : count >= 3 ? 'medium' : 'low',
|
|
333
|
+
occurrences: count,
|
|
334
|
+
extractedAt: new Date().toISOString(),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const patterns = loadJSON(PATTERNS_FILE, []);
|
|
339
|
+
const failingPatterns = patterns.filter(p => p.hits >= 3 && p.successRate < 0.5);
|
|
340
|
+
for (const p of failingPatterns) {
|
|
341
|
+
active.push({
|
|
342
|
+
rule: `Tool sequence [${p.toolSequence.join(' → ')}] for "${p.intent}" fails ${Math.round((1 - p.successRate) * 100)}% of the time — consider alternative approach`,
|
|
343
|
+
source: 'pattern_failure',
|
|
344
|
+
severity: p.successRate < 0.3 ? 'high' : 'medium',
|
|
345
|
+
occurrences: p.hits,
|
|
346
|
+
extractedAt: new Date().toISOString(),
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
// Sort by severity then occurrences, cap at 10
|
|
350
|
+
const severityOrder = { high: 3, medium: 2, low: 1 };
|
|
351
|
+
active.sort((a, b) => (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0)
|
|
352
|
+
|| b.occurrences - a.occurrences);
|
|
353
|
+
const finalCorrections = active.slice(0, 10);
|
|
354
|
+
// Save for prompt injection
|
|
355
|
+
ensureDir(MEMORY_DIR);
|
|
356
|
+
saveJSON(CORRECTIONS_FILE, finalCorrections);
|
|
357
|
+
state.stats.correctionsActive = finalCorrections.length;
|
|
358
|
+
saveState(state);
|
|
359
|
+
return finalCorrections;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Format active corrections for system prompt injection.
|
|
363
|
+
* Called by agent.ts to append corrections to the system prompt.
|
|
364
|
+
*/
|
|
365
|
+
export function getActiveCorrectionsPrompt() {
|
|
366
|
+
const corrections = loadJSON(CORRECTIONS_FILE, []);
|
|
367
|
+
if (corrections.length === 0)
|
|
368
|
+
return '';
|
|
369
|
+
const lines = ['[Active Corrections — avoid repeating these mistakes]'];
|
|
370
|
+
for (const c of corrections) {
|
|
371
|
+
const icon = c.severity === 'high' ? '!!' : c.severity === 'medium' ? '!' : '-';
|
|
372
|
+
lines.push(`${icon} ${c.rule} (${c.occurrences}x, from ${c.source})`);
|
|
373
|
+
}
|
|
374
|
+
return lines.join('\n');
|
|
375
|
+
}
|
|
376
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
377
|
+
// 5. CLOSE REFLECTION → ROUTING LOOP
|
|
378
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
379
|
+
/**
|
|
380
|
+
* Analyze reflections to adjust skill ratings.
|
|
381
|
+
* If an agent consistently fails at a task category,
|
|
382
|
+
* downgrade its mu in that category.
|
|
383
|
+
*/
|
|
384
|
+
export function closeReflectionLoop() {
|
|
385
|
+
const state = loadState();
|
|
386
|
+
const reflections = loadJSON(REFLECTIONS_FILE, []);
|
|
387
|
+
if (reflections.length === 0)
|
|
388
|
+
return 0;
|
|
389
|
+
const routingHistory = loadJSON(ROUTING_HISTORY_FILE, []);
|
|
390
|
+
// Load current skill ratings
|
|
391
|
+
const ratings = loadJSON(SKILL_RATINGS_FILE, {});
|
|
392
|
+
let adjustments = 0;
|
|
393
|
+
// For each reflection, find the responsible agent and category
|
|
394
|
+
for (const r of reflections) {
|
|
395
|
+
const taskWords = r.taskMessage.toLowerCase().split(/\s+/);
|
|
396
|
+
// Find matching routing entry
|
|
397
|
+
const matchingRoute = routingHistory.find(h => {
|
|
398
|
+
const intentWords = h.intent.toLowerCase().split(/\s+/);
|
|
399
|
+
const overlap = taskWords.filter(w => intentWords.includes(w)).length;
|
|
400
|
+
return overlap >= Math.min(3, taskWords.length * 0.5);
|
|
401
|
+
});
|
|
402
|
+
if (!matchingRoute)
|
|
403
|
+
continue;
|
|
404
|
+
const agent = matchingRoute.agent;
|
|
405
|
+
if (!agent || agent === 'auto')
|
|
406
|
+
continue;
|
|
407
|
+
// Detect category from task message
|
|
408
|
+
const categoryKeywords = {
|
|
409
|
+
coding: ['code', 'function', 'implement', 'build', 'component'],
|
|
410
|
+
debugging: ['fix', 'bug', 'error', 'crash', 'debug'],
|
|
411
|
+
security: ['security', 'vulnerability', 'exploit', 'auth', 'encrypt'],
|
|
412
|
+
research: ['research', 'find', 'search', 'compare', 'investigate'],
|
|
413
|
+
writing: ['write', 'document', 'explain', 'describe', 'draft'],
|
|
414
|
+
};
|
|
415
|
+
let detectedCategory = 'general';
|
|
416
|
+
let bestCatScore = 0;
|
|
417
|
+
for (const [cat, keywords] of Object.entries(categoryKeywords)) {
|
|
418
|
+
const score = keywords.filter(k => taskWords.includes(k)).length;
|
|
419
|
+
if (score > bestCatScore) {
|
|
420
|
+
bestCatScore = score;
|
|
421
|
+
detectedCategory = cat;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// Adjust rating: penalize the agent in this category
|
|
425
|
+
if (!ratings[agent])
|
|
426
|
+
ratings[agent] = { _overall: { mu: 25, sigma: 8.33 } };
|
|
427
|
+
if (!ratings[agent][detectedCategory]) {
|
|
428
|
+
ratings[agent][detectedCategory] = { mu: 25, sigma: 8.33 };
|
|
429
|
+
}
|
|
430
|
+
const current = ratings[agent][detectedCategory];
|
|
431
|
+
// Apply a small penalty (Bradley-Terry loss equivalent)
|
|
432
|
+
const K = current.sigma * current.sigma / Math.sqrt(2 * (current.sigma / 2) ** 2 + current.sigma ** 2);
|
|
433
|
+
current.mu = Math.max(15, current.mu - K * 0.5); // Half-strength penalty
|
|
434
|
+
current.sigma = Math.max(0.5, current.sigma * 0.95); // Reduce uncertainty slightly
|
|
435
|
+
adjustments++;
|
|
436
|
+
}
|
|
437
|
+
if (adjustments > 0) {
|
|
438
|
+
saveJSON(SKILL_RATINGS_FILE, ratings);
|
|
439
|
+
state.stats.reflectionsClosed += adjustments;
|
|
440
|
+
saveState(state);
|
|
441
|
+
}
|
|
442
|
+
return adjustments;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Transfer successful patterns from one project to another.
|
|
446
|
+
* Patterns with high success rates in one project get added
|
|
447
|
+
* to the global pool with reduced confidence.
|
|
448
|
+
*/
|
|
449
|
+
export function crossPollinatePatterns() {
|
|
450
|
+
const state = loadState();
|
|
451
|
+
const projects = loadJSON(PROJECTS_FILE, []);
|
|
452
|
+
if (projects.length < 2)
|
|
453
|
+
return 0;
|
|
454
|
+
const patterns = loadJSON(PATTERNS_FILE, []);
|
|
455
|
+
// Find patterns that are project-specific (high success, mention project files)
|
|
456
|
+
const globalIntents = new Set(patterns.map(p => p.intent));
|
|
457
|
+
let transferred = 0;
|
|
458
|
+
// Look for patterns that use tools common across projects
|
|
459
|
+
const universalTools = ['read_file', 'write_file', 'bash', 'grep', 'glob', 'edit_file'];
|
|
460
|
+
const universalPatterns = patterns.filter(p => p.hits >= 3 &&
|
|
461
|
+
p.successRate >= 0.7 &&
|
|
462
|
+
p.toolSequence.every(t => universalTools.includes(t)) &&
|
|
463
|
+
!p.origin);
|
|
464
|
+
// For each universal pattern, check if a variant exists
|
|
465
|
+
for (const p of universalPatterns) {
|
|
466
|
+
// Create a generic version with reduced confidence
|
|
467
|
+
const genericIntent = p.intent
|
|
468
|
+
.replace(/\b(src|lib|app|packages|components)\b/g, '')
|
|
469
|
+
.replace(/\s+/g, ' ')
|
|
470
|
+
.trim();
|
|
471
|
+
if (genericIntent.length < 5)
|
|
472
|
+
continue;
|
|
473
|
+
if (globalIntents.has(genericIntent))
|
|
474
|
+
continue;
|
|
475
|
+
// Add as cross-pollinated pattern with lower confidence
|
|
476
|
+
patterns.push({
|
|
477
|
+
intent: genericIntent,
|
|
478
|
+
keywords: p.keywords,
|
|
479
|
+
toolSequence: p.toolSequence,
|
|
480
|
+
hits: 1,
|
|
481
|
+
successRate: p.successRate * 0.7, // 30% confidence reduction
|
|
482
|
+
avgTokensSaved: p.avgTokensSaved,
|
|
483
|
+
origin: 'cross-project',
|
|
484
|
+
});
|
|
485
|
+
globalIntents.add(genericIntent);
|
|
486
|
+
transferred++;
|
|
487
|
+
if (transferred >= 10)
|
|
488
|
+
break; // Max 10 per cycle
|
|
489
|
+
}
|
|
490
|
+
if (transferred > 0) {
|
|
491
|
+
saveJSON(PATTERNS_FILE, patterns);
|
|
492
|
+
state.crossPollinatedCount += transferred;
|
|
493
|
+
state.stats.patternsTransferred += transferred;
|
|
494
|
+
saveState(state);
|
|
495
|
+
}
|
|
496
|
+
return transferred;
|
|
497
|
+
}
|
|
498
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
499
|
+
// 7. BUILD SKILL MAP
|
|
500
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
501
|
+
const INITIAL_MU = 25.0;
|
|
502
|
+
const INITIAL_SIGMA = INITIAL_MU / 3;
|
|
503
|
+
/**
|
|
504
|
+
* Build a human-readable skill map from Bayesian ratings.
|
|
505
|
+
* Classifies agents as proven (low σ), developing (medium σ), or untested (high σ).
|
|
506
|
+
*/
|
|
507
|
+
export function buildSkillMap() {
|
|
508
|
+
const state = loadState();
|
|
509
|
+
const ratings = loadJSON(SKILL_RATINGS_FILE, {});
|
|
510
|
+
const map = [];
|
|
511
|
+
for (const [agent, agentRatings] of Object.entries(ratings)) {
|
|
512
|
+
const overall = agentRatings._overall || { mu: INITIAL_MU, sigma: INITIAL_SIGMA };
|
|
513
|
+
// Classify status
|
|
514
|
+
let status;
|
|
515
|
+
if (overall.sigma <= 2)
|
|
516
|
+
status = 'proven';
|
|
517
|
+
else if (overall.sigma <= 5)
|
|
518
|
+
status = 'developing';
|
|
519
|
+
else
|
|
520
|
+
status = 'untested';
|
|
521
|
+
// Build category map (exclude _overall)
|
|
522
|
+
const categories = {};
|
|
523
|
+
for (const [cat, rating] of Object.entries(agentRatings)) {
|
|
524
|
+
if (cat === '_overall')
|
|
525
|
+
continue;
|
|
526
|
+
categories[cat] = {
|
|
527
|
+
mu: Math.round(rating.mu * 10) / 10,
|
|
528
|
+
sigma: Math.round(rating.sigma * 10) / 10,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
// Confidence label
|
|
532
|
+
const confidence = overall.sigma <= 1 ? 'very high'
|
|
533
|
+
: overall.sigma <= 3 ? 'high'
|
|
534
|
+
: overall.sigma <= 5 ? 'medium'
|
|
535
|
+
: overall.sigma <= 7 ? 'low'
|
|
536
|
+
: 'untested';
|
|
537
|
+
map.push({
|
|
538
|
+
agent,
|
|
539
|
+
overall: {
|
|
540
|
+
mu: Math.round(overall.mu * 10) / 10,
|
|
541
|
+
sigma: Math.round(overall.sigma * 10) / 10,
|
|
542
|
+
confidence,
|
|
543
|
+
},
|
|
544
|
+
categories,
|
|
545
|
+
status,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
// Sort by overall mu descending
|
|
549
|
+
map.sort((a, b) => b.overall.mu - a.overall.mu);
|
|
550
|
+
state.skillMap = map;
|
|
551
|
+
saveJSON(SKILL_MAP_FILE, map);
|
|
552
|
+
saveState(state);
|
|
553
|
+
return map;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Format skill map for terminal display.
|
|
557
|
+
*/
|
|
558
|
+
export function formatSkillMap(map) {
|
|
559
|
+
if (map.length === 0)
|
|
560
|
+
return 'No skill data yet.';
|
|
561
|
+
const lines = [
|
|
562
|
+
'┌────────────────────────────────────────────────────────────┐',
|
|
563
|
+
'│ AGENT SKILL MAP (Bayesian μ ± 2σ) │',
|
|
564
|
+
'├──────────────────┬────────┬──────┬────────────┬───────────┤',
|
|
565
|
+
'│ Agent │ μ │ σ │ Confidence │ Status │',
|
|
566
|
+
'├──────────────────┼────────┼──────┼────────────┼───────────┤',
|
|
567
|
+
];
|
|
568
|
+
let proven = 0;
|
|
569
|
+
let developing = 0;
|
|
570
|
+
let untested = 0;
|
|
571
|
+
for (const entry of map) {
|
|
572
|
+
const agentPad = entry.agent.padEnd(16).slice(0, 16);
|
|
573
|
+
const muPad = entry.overall.mu.toFixed(1).padStart(6);
|
|
574
|
+
const sigmaPad = entry.overall.sigma.toFixed(1).padStart(4);
|
|
575
|
+
const confPad = entry.overall.confidence.padEnd(10).slice(0, 10);
|
|
576
|
+
const statusIcon = entry.status === 'proven' ? '★' : entry.status === 'developing' ? '◆' : '○';
|
|
577
|
+
const statusPad = `${statusIcon} ${entry.status}`.padEnd(9);
|
|
578
|
+
lines.push(`│ ${agentPad} │ ${muPad} │ ${sigmaPad} │ ${confPad} │ ${statusPad} │`);
|
|
579
|
+
if (entry.status === 'proven')
|
|
580
|
+
proven++;
|
|
581
|
+
else if (entry.status === 'developing')
|
|
582
|
+
developing++;
|
|
583
|
+
else
|
|
584
|
+
untested++;
|
|
585
|
+
// Show top category specializations
|
|
586
|
+
const cats = Object.entries(entry.categories)
|
|
587
|
+
.sort((a, b) => b[1].mu - a[1].mu)
|
|
588
|
+
.slice(0, 2);
|
|
589
|
+
if (cats.length > 0) {
|
|
590
|
+
const catStr = cats.map(([c, r]) => `${c}:${r.mu.toFixed(1)}`).join(', ');
|
|
591
|
+
lines.push(`│ └─ ${catStr.padEnd(50).slice(0, 50)} │`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
lines.push('├──────────────────────────────────────────────────────────┤');
|
|
595
|
+
lines.push(`│ ★ Proven: ${proven} ◆ Developing: ${developing} ○ Untested: ${untested}`.padEnd(59) + '│');
|
|
596
|
+
lines.push('└────────────────────────────────────────────────────────────┘');
|
|
597
|
+
return lines.join('\n');
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Analyze engagement outcomes from discovery actions
|
|
601
|
+
* and produce topic weights for the next opportunity cycle.
|
|
602
|
+
*/
|
|
603
|
+
export function feedEngagementBack(discoveryDir) {
|
|
604
|
+
const state = loadState();
|
|
605
|
+
const postedFile = join(discoveryDir, 'actions', 'posted.json');
|
|
606
|
+
if (!existsSync(postedFile))
|
|
607
|
+
return state.topicWeights;
|
|
608
|
+
const posted = loadJSON(postedFile, []);
|
|
609
|
+
if (posted.length === 0)
|
|
610
|
+
return state.topicWeights;
|
|
611
|
+
// Count engagement by topic
|
|
612
|
+
const topicEngagement = {};
|
|
613
|
+
for (const entry of posted) {
|
|
614
|
+
const topic = entry.topic || 'general';
|
|
615
|
+
if (!topicEngagement[topic])
|
|
616
|
+
topicEngagement[topic] = { engaged: 0, ignored: 0 };
|
|
617
|
+
if (entry.engagement === 'engaged' || (entry.currentScore || 0) > (entry.scoreAtPost || 0)) {
|
|
618
|
+
topicEngagement[topic].engaged++;
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
topicEngagement[topic].ignored++;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
// Calculate weights
|
|
625
|
+
const weights = [];
|
|
626
|
+
for (const [topic, stats] of Object.entries(topicEngagement)) {
|
|
627
|
+
const total = stats.engaged + stats.ignored;
|
|
628
|
+
const weight = total > 0 ? stats.engaged / total : 0.5;
|
|
629
|
+
weights.push({
|
|
630
|
+
topic,
|
|
631
|
+
weight: Math.round(weight * 100) / 100,
|
|
632
|
+
engaged: stats.engaged,
|
|
633
|
+
ignored: stats.ignored,
|
|
634
|
+
updatedAt: new Date().toISOString(),
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
// Sort by weight descending
|
|
638
|
+
weights.sort((a, b) => b.weight - a.weight);
|
|
639
|
+
// Save for discovery daemon to consume
|
|
640
|
+
const topicWeightsFile = join(discoveryDir, 'topic-weights.json');
|
|
641
|
+
if (existsSync(discoveryDir)) {
|
|
642
|
+
saveJSON(topicWeightsFile, weights);
|
|
643
|
+
}
|
|
644
|
+
state.topicWeights = weights;
|
|
645
|
+
state.stats.engagementsFedBack += weights.length;
|
|
646
|
+
saveState(state);
|
|
647
|
+
return weights;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Run the full synthesis cycle:
|
|
651
|
+
* 1. Consume discovered tools
|
|
652
|
+
* 2. Instantiate proposed agents
|
|
653
|
+
* 3. Extract paper insights
|
|
654
|
+
* 4. Build active corrections
|
|
655
|
+
* 5. Close reflection loop
|
|
656
|
+
* 6. Cross-pollinate patterns
|
|
657
|
+
* 7. Build skill map
|
|
658
|
+
* 8. Feed engagement back
|
|
659
|
+
*
|
|
660
|
+
* All operations are heuristic — no LLM calls.
|
|
661
|
+
* Safe to call frequently — each operation is idempotent.
|
|
662
|
+
*/
|
|
663
|
+
export function synthesize(discoveryDir) {
|
|
664
|
+
const state = loadState();
|
|
665
|
+
state.totalCycles++;
|
|
666
|
+
state.lastCycleAt = new Date().toISOString();
|
|
667
|
+
saveState(state);
|
|
668
|
+
// Auto-detect discovery dir
|
|
669
|
+
const discDir = discoveryDir || join(process.cwd(), '.kbot-discovery');
|
|
670
|
+
// Run all 8 operations
|
|
671
|
+
const toolAdoptions = existsSync(discDir) ? consumeDiscoveredTools(discDir) : [];
|
|
672
|
+
const agentTrials = existsSync(discDir) ? instantiateProposedAgents(discDir) : [];
|
|
673
|
+
const paperInsights = existsSync(discDir) ? extractPaperInsights(discDir) : [];
|
|
674
|
+
const activeCorrections = buildActiveCorrections();
|
|
675
|
+
const reflectionsClosed = closeReflectionLoop();
|
|
676
|
+
const patternsTransferred = crossPollinatePatterns();
|
|
677
|
+
const skillMap = buildSkillMap();
|
|
678
|
+
const topicWeights = existsSync(discDir) ? feedEngagementBack(discDir) : [];
|
|
679
|
+
return {
|
|
680
|
+
toolAdoptions,
|
|
681
|
+
agentTrials,
|
|
682
|
+
paperInsights,
|
|
683
|
+
activeCorrections,
|
|
684
|
+
reflectionsClosed,
|
|
685
|
+
patternsTransferred,
|
|
686
|
+
skillMap,
|
|
687
|
+
topicWeights,
|
|
688
|
+
cycleNumber: state.totalCycles,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
692
|
+
// Stats & Display
|
|
693
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
694
|
+
/**
|
|
695
|
+
* Get synthesis engine stats for kbot status display.
|
|
696
|
+
*/
|
|
697
|
+
export function getSynthesisEngineStats() {
|
|
698
|
+
const state = loadState();
|
|
699
|
+
return {
|
|
700
|
+
...state.stats,
|
|
701
|
+
totalCycles: state.totalCycles,
|
|
702
|
+
lastCycleAt: state.lastCycleAt,
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Format synthesis results for terminal display.
|
|
707
|
+
*/
|
|
708
|
+
export function formatSynthesisResult(result) {
|
|
709
|
+
const lines = [
|
|
710
|
+
'═══════════════════════════════════════════════════════════════',
|
|
711
|
+
` SYNTHESIS CYCLE #${result.cycleNumber}`,
|
|
712
|
+
'═══════════════════════════════════════════════════════════════',
|
|
713
|
+
'',
|
|
714
|
+
];
|
|
715
|
+
// Tools
|
|
716
|
+
if (result.toolAdoptions.length > 0) {
|
|
717
|
+
lines.push(`## Discovered Tools (${result.toolAdoptions.length} evaluated)`);
|
|
718
|
+
const adopted = result.toolAdoptions.filter(t => t.status === 'adopted');
|
|
719
|
+
const rejected = result.toolAdoptions.filter(t => t.status === 'rejected');
|
|
720
|
+
if (adopted.length > 0) {
|
|
721
|
+
for (const t of adopted) {
|
|
722
|
+
lines.push(` + ADOPT: ${t.name} (${t.stars}★) — ${t.reason}`);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (rejected.length > 0) {
|
|
726
|
+
lines.push(` - Rejected: ${rejected.length} tools (no match to failure patterns)`);
|
|
727
|
+
}
|
|
728
|
+
lines.push('');
|
|
729
|
+
}
|
|
730
|
+
// Agents
|
|
731
|
+
if (result.agentTrials.length > 0) {
|
|
732
|
+
lines.push(`## Agent Trials (${result.agentTrials.length} started)`);
|
|
733
|
+
for (const a of result.agentTrials) {
|
|
734
|
+
lines.push(` ◆ TRIAL: ${a.name} → ${a.targetCategory} (σ gap detected)`);
|
|
735
|
+
}
|
|
736
|
+
lines.push('');
|
|
737
|
+
}
|
|
738
|
+
// Papers
|
|
739
|
+
if (result.paperInsights.length > 0) {
|
|
740
|
+
lines.push(`## Paper Insights (${result.paperInsights.length} extracted)`);
|
|
741
|
+
for (const p of result.paperInsights) {
|
|
742
|
+
const icon = p.status === 'proposed' ? '→' : '×';
|
|
743
|
+
lines.push(` ${icon} "${p.technique}" from "${p.title.slice(0, 50)}..."`);
|
|
744
|
+
lines.push(` Applies to: ${p.applicableTo}`);
|
|
745
|
+
}
|
|
746
|
+
lines.push('');
|
|
747
|
+
}
|
|
748
|
+
// Corrections
|
|
749
|
+
if (result.activeCorrections.length > 0) {
|
|
750
|
+
lines.push(`## Active Corrections (${result.activeCorrections.length} injected into prompts)`);
|
|
751
|
+
for (const c of result.activeCorrections.slice(0, 5)) {
|
|
752
|
+
const icon = c.severity === 'high' ? '!!' : c.severity === 'medium' ? ' !' : ' -';
|
|
753
|
+
lines.push(` ${icon} ${c.rule.slice(0, 70)}...`);
|
|
754
|
+
}
|
|
755
|
+
lines.push('');
|
|
756
|
+
}
|
|
757
|
+
// Reflections
|
|
758
|
+
if (result.reflectionsClosed > 0) {
|
|
759
|
+
lines.push(`## Reflection → Routing: ${result.reflectionsClosed} skill ratings adjusted`);
|
|
760
|
+
lines.push('');
|
|
761
|
+
}
|
|
762
|
+
// Cross-pollination
|
|
763
|
+
if (result.patternsTransferred > 0) {
|
|
764
|
+
lines.push(`## Cross-Pollination: ${result.patternsTransferred} patterns transferred across projects`);
|
|
765
|
+
lines.push('');
|
|
766
|
+
}
|
|
767
|
+
// Skill Map
|
|
768
|
+
lines.push('## Skill Map');
|
|
769
|
+
lines.push(formatSkillMap(result.skillMap));
|
|
770
|
+
lines.push('');
|
|
771
|
+
// Topic Weights
|
|
772
|
+
if (result.topicWeights.length > 0) {
|
|
773
|
+
lines.push(`## Topic Weights (${result.topicWeights.length} topics scored)`);
|
|
774
|
+
for (const tw of result.topicWeights.slice(0, 5)) {
|
|
775
|
+
const bar = '█'.repeat(Math.round(tw.weight * 10)) + '░'.repeat(10 - Math.round(tw.weight * 10));
|
|
776
|
+
lines.push(` ${bar} ${tw.topic} (${tw.engaged} engaged / ${tw.ignored} ignored)`);
|
|
777
|
+
}
|
|
778
|
+
lines.push('');
|
|
779
|
+
}
|
|
780
|
+
lines.push('═══════════════════════════════════════════════════════════════');
|
|
781
|
+
return lines.join('\n');
|
|
782
|
+
}
|
|
783
|
+
//# sourceMappingURL=synthesis-engine.js.map
|