@kernel.chat/kbot 3.64.0 → 3.65.0
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/cli.js +210 -26
- package/dist/dream.d.ts +18 -0
- package/dist/dream.js +185 -7
- package/dist/learning.d.ts +6 -0
- package/dist/learning.js +41 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3167,20 +3167,28 @@ async function main() {
|
|
|
3167
3167
|
.description('Run a dream cycle now (uses local Ollama)')
|
|
3168
3168
|
.action(async () => {
|
|
3169
3169
|
const { dream } = await import('./dream.js');
|
|
3170
|
-
|
|
3170
|
+
console.log();
|
|
3171
|
+
console.log(` ${chalk.hex('#A78BFA')('◆')} ${chalk.bold('Dream Engine')} ${chalk.dim('consolidating memories...')}`);
|
|
3172
|
+
console.log();
|
|
3171
3173
|
const result = await dream();
|
|
3172
3174
|
if (result.success) {
|
|
3173
|
-
|
|
3174
|
-
console.log(
|
|
3175
|
-
console.log(`
|
|
3176
|
-
console.log(`
|
|
3177
|
-
|
|
3175
|
+
console.log(` ${chalk.hex('#4ADE80')('✓')} ${chalk.bold(`Cycle #${result.cycle} complete`)} ${chalk.dim(`${result.duration}ms`)}`);
|
|
3176
|
+
console.log();
|
|
3177
|
+
console.log(` ${chalk.hex('#4ADE80')('+')} ${chalk.bold(String(result.newInsights))} new insights`);
|
|
3178
|
+
console.log(` ${chalk.hex('#A78BFA')('↻')} ${chalk.bold(String(result.reinforced))} reinforced`);
|
|
3179
|
+
if (result.archived > 0) {
|
|
3180
|
+
console.log(` ${chalk.dim('↓')} ${chalk.dim(`${result.archived} archived (aged out)`)}`);
|
|
3181
|
+
}
|
|
3182
|
+
console.log();
|
|
3183
|
+
console.log(` ${chalk.dim('View results:')} ${chalk.hex('#A78BFA')('kbot dream status')}`);
|
|
3178
3184
|
}
|
|
3179
3185
|
else {
|
|
3180
|
-
|
|
3181
|
-
if (result.archived > 0)
|
|
3182
|
-
console.log(`
|
|
3186
|
+
console.log(` ${chalk.hex('#FBBF24')('!')} ${result.error || 'Dream cycle failed'}`);
|
|
3187
|
+
if (result.archived > 0) {
|
|
3188
|
+
console.log(` ${chalk.dim('↓')} ${chalk.dim(`${result.archived} insights archived (aging only)`)}`);
|
|
3189
|
+
}
|
|
3183
3190
|
}
|
|
3191
|
+
console.log();
|
|
3184
3192
|
});
|
|
3185
3193
|
dreamCmd
|
|
3186
3194
|
.command('status')
|
|
@@ -3188,23 +3196,86 @@ async function main() {
|
|
|
3188
3196
|
.action(async () => {
|
|
3189
3197
|
const { getDreamStatus } = await import('./dream.js');
|
|
3190
3198
|
const { state, insights, archiveCount } = getDreamStatus();
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3199
|
+
// ── Helper functions ──
|
|
3200
|
+
const W = 56; // inner width
|
|
3201
|
+
const box = {
|
|
3202
|
+
tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│',
|
|
3203
|
+
pad: (s, w) => {
|
|
3204
|
+
// Pad string to width, accounting for chalk ANSI codes
|
|
3205
|
+
const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
3206
|
+
const diff = w - visible.length;
|
|
3207
|
+
return diff > 0 ? s + ' '.repeat(diff) : s;
|
|
3208
|
+
},
|
|
3209
|
+
};
|
|
3210
|
+
const relevanceBar = (pct, len = 20) => {
|
|
3211
|
+
const filled = Math.round((pct / 100) * len);
|
|
3212
|
+
const empty = len - filled;
|
|
3213
|
+
const color = pct >= 70 ? chalk.hex('#4ADE80') : pct >= 40 ? chalk.hex('#FBBF24') : chalk.hex('#F87171');
|
|
3214
|
+
return color('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
|
|
3215
|
+
};
|
|
3216
|
+
const categoryColor = (cat) => {
|
|
3217
|
+
const colors = {
|
|
3218
|
+
pattern: '#A78BFA', preference: '#67E8F9', skill: '#4ADE80',
|
|
3219
|
+
project: '#FB923C', relationship: '#F472B6',
|
|
3220
|
+
};
|
|
3221
|
+
return chalk.hex(colors[cat] || '#A78BFA');
|
|
3222
|
+
};
|
|
3223
|
+
const categoryChip = (cat) => {
|
|
3224
|
+
const c = categoryColor(cat);
|
|
3225
|
+
return c(` ${cat.toUpperCase()} `);
|
|
3226
|
+
};
|
|
3227
|
+
// ── Header box ──
|
|
3228
|
+
console.log();
|
|
3229
|
+
console.log(chalk.hex('#A78BFA')(` ${box.tl}${box.h.repeat(W)}${box.tr}`));
|
|
3230
|
+
console.log(chalk.hex('#A78BFA')(` ${box.v}`) + box.pad(` ${chalk.hex('#A78BFA').bold('◆ DREAM ENGINE')} ${chalk.dim('memory consolidation')}`, W) + chalk.hex('#A78BFA')(box.v));
|
|
3231
|
+
console.log(chalk.hex('#A78BFA')(` ${box.bl}${box.h.repeat(W)}${box.br}`));
|
|
3232
|
+
console.log();
|
|
3233
|
+
// ── Stats row ──
|
|
3234
|
+
const lastDreamDisplay = state.lastDream
|
|
3235
|
+
? new Date(state.lastDream).toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })
|
|
3236
|
+
: chalk.dim('never');
|
|
3237
|
+
console.log(` ${chalk.bold('Cycles')} ${chalk.hex('#A78BFA')(String(state.cycles))} ${chalk.bold('Last')} ${lastDreamDisplay} ${chalk.bold('Active')} ${chalk.hex('#4ADE80')(String(state.activeInsights))}`);
|
|
3238
|
+
console.log(` ${chalk.bold('Total')} ${chalk.dim(String(state.totalInsights))} ${chalk.bold('Archived')} ${chalk.dim(`${state.totalArchived} (${archiveCount} files)`)}`);
|
|
3239
|
+
console.log();
|
|
3197
3240
|
if (insights.length > 0) {
|
|
3241
|
+
// ── Average relevance bar ──
|
|
3198
3242
|
const avgRel = Math.round(insights.reduce((s, i) => s + i.relevance, 0) / insights.length * 100);
|
|
3199
|
-
console.log(`Avg
|
|
3200
|
-
console.log(
|
|
3243
|
+
console.log(` ${chalk.bold('Avg Relevance')} ${relevanceBar(avgRel, 24)} ${chalk.bold(`${avgRel}%`)}`);
|
|
3244
|
+
console.log();
|
|
3245
|
+
// ── Category breakdown ──
|
|
3246
|
+
const catCounts = {};
|
|
3247
|
+
for (const i of insights)
|
|
3248
|
+
catCounts[i.category] = (catCounts[i.category] || 0) + 1;
|
|
3249
|
+
const chips = Object.entries(catCounts)
|
|
3250
|
+
.sort((a, b) => b[1] - a[1])
|
|
3251
|
+
.map(([cat, count]) => `${categoryChip(cat)} ${chalk.dim(`${count}`)}`)
|
|
3252
|
+
.join(' ');
|
|
3253
|
+
console.log(` ${chalk.bold('Categories')} ${chips}`);
|
|
3254
|
+
console.log();
|
|
3255
|
+
// ── Divider ──
|
|
3256
|
+
console.log(chalk.dim(` ${'─'.repeat(W)}`));
|
|
3257
|
+
console.log();
|
|
3258
|
+
// ── Top insights ──
|
|
3259
|
+
console.log(` ${chalk.bold('Top Insights')}`);
|
|
3260
|
+
console.log();
|
|
3201
3261
|
for (const i of insights.slice(0, 8)) {
|
|
3202
3262
|
const pct = Math.round(i.relevance * 100);
|
|
3203
|
-
|
|
3263
|
+
const bar = relevanceBar(pct, 12);
|
|
3264
|
+
const tag = categoryChip(i.category);
|
|
3265
|
+
console.log(` ${bar} ${chalk.bold(`${pct}%`)} ${tag}`);
|
|
3266
|
+
console.log(` ${chalk.white(i.content)}`);
|
|
3267
|
+
if (i.keywords.length > 0) {
|
|
3268
|
+
console.log(` ${chalk.dim(i.keywords.map(k => `#${k}`).join(' '))} ${chalk.dim('·')} ${chalk.dim(`${i.sessions} sessions`)}`);
|
|
3269
|
+
}
|
|
3270
|
+
console.log();
|
|
3204
3271
|
}
|
|
3205
3272
|
}
|
|
3206
3273
|
else {
|
|
3207
|
-
console.log(chalk.dim('
|
|
3274
|
+
console.log(chalk.dim(` ${'─'.repeat(W)}`));
|
|
3275
|
+
console.log();
|
|
3276
|
+
console.log(` ${chalk.dim('No insights yet.')}`);
|
|
3277
|
+
console.log(` ${chalk.dim('Run:')} ${chalk.hex('#A78BFA')('kbot dream run')} ${chalk.dim('to start consolidating memories')}`);
|
|
3278
|
+
console.log();
|
|
3208
3279
|
}
|
|
3209
3280
|
});
|
|
3210
3281
|
dreamCmd
|
|
@@ -3213,24 +3284,137 @@ async function main() {
|
|
|
3213
3284
|
.action(async (query) => {
|
|
3214
3285
|
const { searchDreams } = await import('./dream.js');
|
|
3215
3286
|
const results = searchDreams(query);
|
|
3287
|
+
// Helpers
|
|
3288
|
+
const relevanceBar = (pct, len = 12) => {
|
|
3289
|
+
const filled = Math.round((pct / 100) * len);
|
|
3290
|
+
const empty = len - filled;
|
|
3291
|
+
const color = pct >= 70 ? chalk.hex('#4ADE80') : pct >= 40 ? chalk.hex('#FBBF24') : chalk.hex('#F87171');
|
|
3292
|
+
return color('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
|
|
3293
|
+
};
|
|
3294
|
+
const categoryColor = (cat) => {
|
|
3295
|
+
const colors = {
|
|
3296
|
+
pattern: '#A78BFA', preference: '#67E8F9', skill: '#4ADE80',
|
|
3297
|
+
project: '#FB923C', relationship: '#F472B6',
|
|
3298
|
+
};
|
|
3299
|
+
return chalk.hex(colors[cat] || '#A78BFA');
|
|
3300
|
+
};
|
|
3301
|
+
const highlightQuery = (text, q) => {
|
|
3302
|
+
const terms = q.toLowerCase().split(/\s+/);
|
|
3303
|
+
let result = text;
|
|
3304
|
+
for (const term of terms) {
|
|
3305
|
+
const regex = new RegExp(`(${term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
3306
|
+
result = result.replace(regex, chalk.hex('#FBBF24').bold.underline('$1'));
|
|
3307
|
+
}
|
|
3308
|
+
return result;
|
|
3309
|
+
};
|
|
3216
3310
|
if (results.length === 0) {
|
|
3217
|
-
|
|
3311
|
+
console.log();
|
|
3312
|
+
console.log(` ${chalk.hex('#A78BFA')('◆')} ${chalk.bold('Dream Search')} ${chalk.dim(`"${query}"`)}`);
|
|
3313
|
+
console.log();
|
|
3314
|
+
console.log(` ${chalk.dim('No insights match this query.')}`);
|
|
3315
|
+
console.log(` ${chalk.dim('Try broader keywords or run')} ${chalk.hex('#A78BFA')('kbot dream run')} ${chalk.dim('first.')}`);
|
|
3316
|
+
console.log();
|
|
3218
3317
|
return;
|
|
3219
3318
|
}
|
|
3220
|
-
console.log(
|
|
3319
|
+
console.log();
|
|
3320
|
+
console.log(` ${chalk.hex('#A78BFA')('◆')} ${chalk.bold('Dream Search')} ${chalk.dim(`"${query}"`)} ${chalk.hex('#4ADE80')(`${results.length} found`)}`);
|
|
3321
|
+
console.log(chalk.dim(` ${'─'.repeat(52)}`));
|
|
3322
|
+
console.log();
|
|
3221
3323
|
for (const i of results.slice(0, 15)) {
|
|
3222
3324
|
const pct = Math.round(i.relevance * 100);
|
|
3223
|
-
|
|
3224
|
-
|
|
3325
|
+
const bar = relevanceBar(pct);
|
|
3326
|
+
const tag = categoryColor(i.category)(` ${i.category.toUpperCase()} `);
|
|
3327
|
+
console.log(` ${bar} ${chalk.bold(`${pct}%`)} ${tag}`);
|
|
3328
|
+
console.log(` ${highlightQuery(i.content, query)}`);
|
|
3329
|
+
const keywordsHighlighted = i.keywords.map(k => highlightQuery(`#${k}`, query)).join(' ');
|
|
3330
|
+
console.log(` ${keywordsHighlighted} ${chalk.dim('·')} ${chalk.dim(`${i.sessions} sessions`)} ${chalk.dim('·')} ${chalk.dim(i.created.split('T')[0])}`);
|
|
3331
|
+
console.log();
|
|
3225
3332
|
}
|
|
3226
3333
|
});
|
|
3227
3334
|
dreamCmd
|
|
3228
3335
|
.command('journal')
|
|
3229
3336
|
.description('Print the full dream journal')
|
|
3230
3337
|
.action(async () => {
|
|
3231
|
-
const {
|
|
3232
|
-
const
|
|
3233
|
-
|
|
3338
|
+
const { getDreamStatus } = await import('./dream.js');
|
|
3339
|
+
const { state, insights } = getDreamStatus();
|
|
3340
|
+
// Helpers
|
|
3341
|
+
const W = 56;
|
|
3342
|
+
const box = {
|
|
3343
|
+
tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│',
|
|
3344
|
+
pad: (s, w) => {
|
|
3345
|
+
const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
3346
|
+
const diff = w - visible.length;
|
|
3347
|
+
return diff > 0 ? s + ' '.repeat(diff) : s;
|
|
3348
|
+
},
|
|
3349
|
+
};
|
|
3350
|
+
const relevanceBar = (pct, len = 16) => {
|
|
3351
|
+
const filled = Math.round((pct / 100) * len);
|
|
3352
|
+
const empty = len - filled;
|
|
3353
|
+
const color = pct >= 70 ? chalk.hex('#4ADE80') : pct >= 40 ? chalk.hex('#FBBF24') : chalk.hex('#F87171');
|
|
3354
|
+
return color('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
|
|
3355
|
+
};
|
|
3356
|
+
const categoryColors = {
|
|
3357
|
+
pattern: '#A78BFA', preference: '#67E8F9', skill: '#4ADE80',
|
|
3358
|
+
project: '#FB923C', relationship: '#F472B6',
|
|
3359
|
+
};
|
|
3360
|
+
const categoryIcon = {
|
|
3361
|
+
pattern: '◇', preference: '♡', skill: '⚡', project: '▸', relationship: '◈',
|
|
3362
|
+
};
|
|
3363
|
+
const categoryColor = (cat) => chalk.hex(categoryColors[cat] || '#A78BFA');
|
|
3364
|
+
if (insights.length === 0) {
|
|
3365
|
+
console.log();
|
|
3366
|
+
console.log(` ${chalk.hex('#A78BFA')('◆')} ${chalk.bold('Dream Journal')}`);
|
|
3367
|
+
console.log();
|
|
3368
|
+
console.log(` ${chalk.dim('The journal is empty.')}`);
|
|
3369
|
+
console.log(` ${chalk.dim('Run')} ${chalk.hex('#A78BFA')('kbot dream run')} ${chalk.dim('after a session to consolidate memories.')}`);
|
|
3370
|
+
console.log();
|
|
3371
|
+
return;
|
|
3372
|
+
}
|
|
3373
|
+
// ── Header ──
|
|
3374
|
+
console.log();
|
|
3375
|
+
console.log(chalk.hex('#A78BFA')(` ${box.tl}${box.h.repeat(W)}${box.tr}`));
|
|
3376
|
+
const headerContent = ` ${chalk.hex('#A78BFA').bold('◆ DREAM JOURNAL')} ${chalk.dim(`${insights.length} insights · cycle ${state.cycles}`)}`;
|
|
3377
|
+
console.log(chalk.hex('#A78BFA')(` ${box.v}`) + box.pad(headerContent, W) + chalk.hex('#A78BFA')(box.v));
|
|
3378
|
+
console.log(chalk.hex('#A78BFA')(` ${box.bl}${box.h.repeat(W)}${box.br}`));
|
|
3379
|
+
console.log();
|
|
3380
|
+
// ── Group by category ──
|
|
3381
|
+
const grouped = {};
|
|
3382
|
+
for (const i of insights) {
|
|
3383
|
+
if (!grouped[i.category])
|
|
3384
|
+
grouped[i.category] = [];
|
|
3385
|
+
grouped[i.category].push(i);
|
|
3386
|
+
}
|
|
3387
|
+
// Sort categories by total insight count descending
|
|
3388
|
+
const catOrder = Object.entries(grouped).sort((a, b) => b[1].length - a[1].length);
|
|
3389
|
+
for (const [cat, catInsights] of catOrder) {
|
|
3390
|
+
const icon = categoryIcon[cat] || '●';
|
|
3391
|
+
const cc = categoryColor(cat);
|
|
3392
|
+
// ── Category section header ──
|
|
3393
|
+
console.log(` ${cc(`${icon} ${cat.toUpperCase()}`)} ${chalk.dim(`(${catInsights.length})`)}`);
|
|
3394
|
+
console.log(` ${cc('─'.repeat(W))}`);
|
|
3395
|
+
console.log();
|
|
3396
|
+
for (const i of catInsights) {
|
|
3397
|
+
const pct = Math.round(i.relevance * 100);
|
|
3398
|
+
const bar = relevanceBar(pct);
|
|
3399
|
+
const date = i.created.split('T')[0];
|
|
3400
|
+
const reinforced = i.lastReinforced !== i.created
|
|
3401
|
+
? chalk.dim(` · reinforced ${i.lastReinforced.split('T')[0]}`)
|
|
3402
|
+
: '';
|
|
3403
|
+
// Card: relevance bar + content + metadata
|
|
3404
|
+
console.log(` ${bar} ${chalk.bold(`${pct}%`)} ${chalk.dim(`${i.sessions} sessions`)}${reinforced}`);
|
|
3405
|
+
console.log(` ${chalk.white(i.content)}`);
|
|
3406
|
+
if (i.keywords.length > 0) {
|
|
3407
|
+
console.log(` ${chalk.dim(i.keywords.map(k => `#${k}`).join(' '))}`);
|
|
3408
|
+
}
|
|
3409
|
+
console.log(` ${chalk.dim(`${date} · ${i.source} · ${i.id}`)}`);
|
|
3410
|
+
console.log();
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
// ── Footer ──
|
|
3414
|
+
console.log(chalk.dim(` ${'─'.repeat(W)}`));
|
|
3415
|
+
const avgRel = Math.round(insights.reduce((s, i) => s + i.relevance, 0) / insights.length * 100);
|
|
3416
|
+
console.log(` ${chalk.dim(`${insights.length} active insights · avg relevance ${avgRel}% · ${state.totalArchived} archived`)}`);
|
|
3417
|
+
console.log();
|
|
3234
3418
|
});
|
|
3235
3419
|
program.parse(process.argv);
|
|
3236
3420
|
const opts = program.opts();
|
package/dist/dream.d.ts
CHANGED
|
@@ -38,6 +38,22 @@ export declare function ageMemories(insights: DreamInsight[]): {
|
|
|
38
38
|
aged: DreamInsight[];
|
|
39
39
|
archived: DreamInsight[];
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* Apply dream insights back into the learning system.
|
|
43
|
+
* This is the feedback loop that makes the memory cascade bidirectional:
|
|
44
|
+
* - "preference" insights → update user profile via learnFact()
|
|
45
|
+
* - "pattern" insights → hint the pattern cache via recordPattern()
|
|
46
|
+
* - "skill" insights → record as observed knowledge
|
|
47
|
+
* - "project" insights → record as project context
|
|
48
|
+
*
|
|
49
|
+
* Called at the end of every dream cycle after new insights are extracted.
|
|
50
|
+
*/
|
|
51
|
+
export declare function applyDreamInsights(insights: DreamInsight[]): ApplyResult;
|
|
52
|
+
export interface ApplyResult {
|
|
53
|
+
preferencesApplied: number;
|
|
54
|
+
patternsHinted: number;
|
|
55
|
+
factsLearned: number;
|
|
56
|
+
}
|
|
41
57
|
/** Run a full dream cycle — consolidate, reinforce, age */
|
|
42
58
|
export declare function dream(sessionId?: string): Promise<DreamResult>;
|
|
43
59
|
export interface DreamResult {
|
|
@@ -48,6 +64,8 @@ export interface DreamResult {
|
|
|
48
64
|
cycle: number;
|
|
49
65
|
duration: number;
|
|
50
66
|
error: string | null;
|
|
67
|
+
/** Feedback from applying insights back into learning tiers */
|
|
68
|
+
applied: ApplyResult | null;
|
|
51
69
|
}
|
|
52
70
|
/** Get dream insights for inclusion in system prompt */
|
|
53
71
|
export declare function getDreamPrompt(maxInsights?: number): string;
|
package/dist/dream.js
CHANGED
|
@@ -19,6 +19,8 @@ import { join } from 'node:path';
|
|
|
19
19
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
20
20
|
import { getHistory } from './memory.js';
|
|
21
21
|
import { loadMemory } from './memory.js';
|
|
22
|
+
import { getTopPatterns, getTopSolutions, getProfileSummary, updateProfile, recordPattern, learnFact, } from './learning.js';
|
|
23
|
+
import { getMemoryScannerStats } from './memory-scanner.js';
|
|
22
24
|
// ── Constants ──
|
|
23
25
|
const DREAM_DIR = join(homedir(), '.kbot', 'memory', 'dreams');
|
|
24
26
|
const JOURNAL_FILE = join(DREAM_DIR, 'journal.json');
|
|
@@ -149,22 +151,61 @@ function buildConsolidationPrompt(sessionHistory, existingInsights, existingMemo
|
|
|
149
151
|
const existingText = existingInsights.length > 0
|
|
150
152
|
? existingInsights.slice(0, 20).map(i => `- [${i.category}] ${i.content}`).join('\n')
|
|
151
153
|
: '(none yet)';
|
|
152
|
-
|
|
154
|
+
// ── Tier 1: Pattern cache — intent → tool sequences ──
|
|
155
|
+
const topPatterns = getTopPatterns(10);
|
|
156
|
+
const patternsText = topPatterns.length > 0
|
|
157
|
+
? topPatterns.map(p => `- "${p.intent}" → ${p.toolSequence.join(' → ')} (${p.hits}x, ${Math.round(p.successRate * 100)}% success)`).join('\n')
|
|
158
|
+
: '(no patterns yet)';
|
|
159
|
+
// ── Tier 2: Solution index — Q&A pairs ──
|
|
160
|
+
const topSolutions = getTopSolutions(5);
|
|
161
|
+
const solutionsText = topSolutions.length > 0
|
|
162
|
+
? topSolutions.map(s => `- Q: ${s.question.slice(0, 100)} → confidence: ${Math.round(s.confidence * 100)}%, reused ${s.reuses}x`).join('\n')
|
|
163
|
+
: '(no solutions yet)';
|
|
164
|
+
// ── Tier 3: User profile ──
|
|
165
|
+
const profileText = getProfileSummary() || '(no profile data yet)';
|
|
166
|
+
// ── Tier 5: Memory scanner — recent passive detections ──
|
|
167
|
+
const scannerStats = getMemoryScannerStats();
|
|
168
|
+
const scannerText = scannerStats.recentDetections.length > 0
|
|
169
|
+
? scannerStats.recentDetections
|
|
170
|
+
.slice(-5)
|
|
171
|
+
.map(d => `- [${d.kind}] ${d.content.slice(0, 150)} (confidence: ${Math.round(d.confidence * 100)}%)`)
|
|
172
|
+
.join('\n')
|
|
173
|
+
: '(no recent detections)';
|
|
174
|
+
return `You are a memory consolidation system. Analyze this conversation session and ALL accumulated knowledge tiers to extract durable cross-tier insights.
|
|
153
175
|
|
|
154
|
-
EXISTING INSIGHTS:
|
|
176
|
+
EXISTING DREAM INSIGHTS (Tier 4 — Dream Journal):
|
|
155
177
|
${existingText}
|
|
156
178
|
|
|
157
179
|
EXISTING PERSISTENT MEMORY:
|
|
158
180
|
${existingMemory.slice(0, 2000) || '(empty)'}
|
|
159
181
|
|
|
182
|
+
LEARNED PATTERNS (Tier 1 — Pattern Cache):
|
|
183
|
+
${patternsText}
|
|
184
|
+
|
|
185
|
+
PROVEN SOLUTIONS (Tier 2 — Solution Index):
|
|
186
|
+
${solutionsText}
|
|
187
|
+
|
|
188
|
+
USER PROFILE (Tier 3):
|
|
189
|
+
${profileText}
|
|
190
|
+
|
|
191
|
+
RECENT MEMORY SCANNER DETECTIONS (Tier 5 — Passive Detection):
|
|
192
|
+
${scannerText}
|
|
193
|
+
|
|
160
194
|
SESSION TO CONSOLIDATE:
|
|
161
195
|
${historyText}
|
|
162
196
|
|
|
163
197
|
INSTRUCTIONS:
|
|
164
|
-
Extract 1-5 insights
|
|
198
|
+
Extract 1-5 insights by synthesizing across ALL tiers. Each insight should be:
|
|
165
199
|
- Durable (useful beyond this session)
|
|
166
200
|
- Non-obvious (not derivable from reading code)
|
|
167
|
-
-
|
|
201
|
+
- Cross-tier when possible (e.g., a pattern + preference = workflow insight)
|
|
202
|
+
- About the USER, their preferences, patterns, workflows, or project context
|
|
203
|
+
|
|
204
|
+
Pay special attention to:
|
|
205
|
+
- Patterns that confirm or contradict existing insights
|
|
206
|
+
- Scanner corrections that reveal unrecognized preferences
|
|
207
|
+
- Solution clusters that suggest emerging expertise areas
|
|
208
|
+
- Profile trends that indicate shifting priorities
|
|
168
209
|
|
|
169
210
|
Format each insight as a JSON array of objects:
|
|
170
211
|
[
|
|
@@ -199,6 +240,133 @@ If none are reinforced, return: []
|
|
|
199
240
|
|
|
200
241
|
Respond ONLY with the JSON array.`;
|
|
201
242
|
}
|
|
243
|
+
// ── Memory Cascade: Feed Insights Back into Tiers ──
|
|
244
|
+
/**
|
|
245
|
+
* Apply dream insights back into the learning system.
|
|
246
|
+
* This is the feedback loop that makes the memory cascade bidirectional:
|
|
247
|
+
* - "preference" insights → update user profile via learnFact()
|
|
248
|
+
* - "pattern" insights → hint the pattern cache via recordPattern()
|
|
249
|
+
* - "skill" insights → record as observed knowledge
|
|
250
|
+
* - "project" insights → record as project context
|
|
251
|
+
*
|
|
252
|
+
* Called at the end of every dream cycle after new insights are extracted.
|
|
253
|
+
*/
|
|
254
|
+
export function applyDreamInsights(insights) {
|
|
255
|
+
const result = {
|
|
256
|
+
preferencesApplied: 0,
|
|
257
|
+
patternsHinted: 0,
|
|
258
|
+
factsLearned: 0,
|
|
259
|
+
};
|
|
260
|
+
for (const insight of insights) {
|
|
261
|
+
switch (insight.category) {
|
|
262
|
+
case 'preference': {
|
|
263
|
+
// Feed preference insights back into the knowledge base as observed facts.
|
|
264
|
+
// This lets the learning context builder surface them in future prompts.
|
|
265
|
+
learnFact(insight.content, 'preference', 'observed');
|
|
266
|
+
// Also nudge the user profile if the insight mentions style/tech preferences
|
|
267
|
+
const lower = insight.content.toLowerCase();
|
|
268
|
+
if (/\b(?:concise|brief|short)\b/.test(lower)) {
|
|
269
|
+
updateProfile({ taskType: 'prefers-concise' });
|
|
270
|
+
}
|
|
271
|
+
else if (/\b(?:detailed|thorough|verbose)\b/.test(lower)) {
|
|
272
|
+
updateProfile({ taskType: 'prefers-detailed' });
|
|
273
|
+
}
|
|
274
|
+
// Extract any tech terms mentioned in the preference
|
|
275
|
+
const techTerms = extractTechTermsFromInsight(insight.content);
|
|
276
|
+
if (techTerms.length > 0) {
|
|
277
|
+
updateProfile({ techTerms });
|
|
278
|
+
}
|
|
279
|
+
result.preferencesApplied++;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
case 'pattern': {
|
|
283
|
+
// Feed pattern insights into the knowledge base.
|
|
284
|
+
// If the insight describes a tool workflow, hint the pattern cache.
|
|
285
|
+
learnFact(insight.content, 'context', 'observed');
|
|
286
|
+
// Try to extract a tool sequence from the insight text
|
|
287
|
+
// (e.g., "User typically reads files then runs tests" → [read_file, run_tests])
|
|
288
|
+
const toolHints = extractToolHintsFromInsight(insight.content);
|
|
289
|
+
if (toolHints.length >= 2) {
|
|
290
|
+
// Record as a pattern with the insight content as the "intent"
|
|
291
|
+
const intentWords = insight.keywords.join(' ') || insight.content.slice(0, 80);
|
|
292
|
+
recordPattern(intentWords, toolHints, 0);
|
|
293
|
+
}
|
|
294
|
+
result.patternsHinted++;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case 'skill': {
|
|
298
|
+
// Skills are knowledge about what the user is good at or learning.
|
|
299
|
+
learnFact(insight.content, 'context', 'observed');
|
|
300
|
+
result.factsLearned++;
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
case 'project': {
|
|
304
|
+
// Project insights become project-scoped knowledge.
|
|
305
|
+
learnFact(insight.content, 'project', 'observed');
|
|
306
|
+
result.factsLearned++;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
case 'relationship': {
|
|
310
|
+
// Relationship insights (how user interacts, team dynamics) → context facts.
|
|
311
|
+
learnFact(insight.content, 'context', 'observed');
|
|
312
|
+
result.factsLearned++;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
/** Extract tech-related terms from insight text */
|
|
320
|
+
function extractTechTermsFromInsight(text) {
|
|
321
|
+
const techTerms = new Set([
|
|
322
|
+
'react', 'typescript', 'node', 'python', 'rust', 'go', 'docker',
|
|
323
|
+
'api', 'database', 'supabase', 'postgres', 'redis', 'mongodb',
|
|
324
|
+
'css', 'html', 'json', 'sql', 'git', 'npm', 'vite', 'webpack',
|
|
325
|
+
'tailwind', 'next', 'express', 'fastify', 'deno', 'bun',
|
|
326
|
+
'playwright', 'vitest', 'jest', 'eslint', 'prettier',
|
|
327
|
+
'ollama', 'anthropic', 'openai', 'claude', 'gpt',
|
|
328
|
+
'ableton', 'serum', 'splice', 'osc', 'midi',
|
|
329
|
+
]);
|
|
330
|
+
return text.toLowerCase()
|
|
331
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
332
|
+
.split(/\s+/)
|
|
333
|
+
.filter(w => techTerms.has(w));
|
|
334
|
+
}
|
|
335
|
+
/** Try to extract tool names from insight text describing a workflow */
|
|
336
|
+
function extractToolHintsFromInsight(text) {
|
|
337
|
+
const toolNames = new Set([
|
|
338
|
+
'read_file', 'write_file', 'edit_file', 'glob', 'grep', 'bash',
|
|
339
|
+
'git_status', 'git_diff', 'git_commit', 'git_push', 'git_log',
|
|
340
|
+
'run_tests', 'test_run', 'build_run', 'type_check', 'lint_check',
|
|
341
|
+
'web_search', 'url_fetch', 'screenshot', 'browser_navigate',
|
|
342
|
+
'kbot_agent', 'spawn_agent', 'memory_save', 'memory_search',
|
|
343
|
+
'research', 'papers_search', 'github_search',
|
|
344
|
+
]);
|
|
345
|
+
// Match tool-like words (snake_case) in the text
|
|
346
|
+
const words = text.toLowerCase().replace(/[^a-z0-9_\s]/g, ' ').split(/\s+/);
|
|
347
|
+
const found = words.filter(w => toolNames.has(w));
|
|
348
|
+
if (found.length >= 2)
|
|
349
|
+
return found;
|
|
350
|
+
// Fallback: look for verb patterns that map to common tools
|
|
351
|
+
const verbMap = [
|
|
352
|
+
[/\bread(?:s|ing)?\s+(?:file|code|source)/i, 'read_file'],
|
|
353
|
+
[/\bwrit(?:e|es|ing)\s+(?:file|code)/i, 'write_file'],
|
|
354
|
+
[/\bedit(?:s|ing)?\b/i, 'edit_file'],
|
|
355
|
+
[/\bsearch(?:es|ing)?\s+(?:web|online|internet)/i, 'web_search'],
|
|
356
|
+
[/\brun(?:s|ning)?\s+test/i, 'run_tests'],
|
|
357
|
+
[/\bgrep(?:s|ping)?\b/i, 'grep'],
|
|
358
|
+
[/\bgit\s+(?:commit|push|diff|status|log)/i, 'git_commit'],
|
|
359
|
+
[/\bbuild(?:s|ing)?\b/i, 'build_run'],
|
|
360
|
+
[/\bbash\b|\bshell\b|\bcommand\b/i, 'bash'],
|
|
361
|
+
];
|
|
362
|
+
const mapped = [];
|
|
363
|
+
for (const [pattern, tool] of verbMap) {
|
|
364
|
+
if (pattern.test(text) && !mapped.includes(tool)) {
|
|
365
|
+
mapped.push(tool);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return mapped;
|
|
369
|
+
}
|
|
202
370
|
// ── Core Dream Functions ──
|
|
203
371
|
/** Run a full dream cycle — consolidate, reinforce, age */
|
|
204
372
|
export async function dream(sessionId = 'default') {
|
|
@@ -210,6 +378,7 @@ export async function dream(sessionId = 'default') {
|
|
|
210
378
|
cycle: 0,
|
|
211
379
|
duration: 0,
|
|
212
380
|
error: null,
|
|
381
|
+
applied: null,
|
|
213
382
|
};
|
|
214
383
|
const start = Date.now();
|
|
215
384
|
// Check Ollama availability
|
|
@@ -241,9 +410,10 @@ export async function dream(sessionId = 'default') {
|
|
|
241
410
|
archiveInsights(archived);
|
|
242
411
|
journal = aged;
|
|
243
412
|
result.archived = archived.length;
|
|
244
|
-
// Phase 2: Extract new insights from session
|
|
413
|
+
// Phase 2: Extract new insights from session (cross-tier consolidation)
|
|
245
414
|
const consolidationPrompt = buildConsolidationPrompt(history, journal, memory);
|
|
246
415
|
const rawInsights = await ollamaGenerate(consolidationPrompt);
|
|
416
|
+
const newlyCreatedInsights = [];
|
|
247
417
|
if (rawInsights) {
|
|
248
418
|
try {
|
|
249
419
|
const parsed = JSON.parse(rawInsights);
|
|
@@ -255,7 +425,7 @@ export async function dream(sessionId = 'default') {
|
|
|
255
425
|
p.content.toLowerCase().includes(j.content.toLowerCase().slice(0, 50)));
|
|
256
426
|
if (isDupe)
|
|
257
427
|
continue;
|
|
258
|
-
|
|
428
|
+
const insight = {
|
|
259
429
|
id: generateId(),
|
|
260
430
|
content: p.content,
|
|
261
431
|
category: p.category || 'pattern',
|
|
@@ -265,7 +435,9 @@ export async function dream(sessionId = 'default') {
|
|
|
265
435
|
created: now,
|
|
266
436
|
lastReinforced: now,
|
|
267
437
|
source: `session_${state.cycles + 1}`,
|
|
268
|
-
}
|
|
438
|
+
};
|
|
439
|
+
journal.push(insight);
|
|
440
|
+
newlyCreatedInsights.push(insight);
|
|
269
441
|
result.newInsights++;
|
|
270
442
|
}
|
|
271
443
|
}
|
|
@@ -306,6 +478,12 @@ export async function dream(sessionId = 'default') {
|
|
|
306
478
|
journal = journal.slice(0, MAX_INSIGHTS);
|
|
307
479
|
result.archived += overflow.length;
|
|
308
480
|
}
|
|
481
|
+
// Phase 5: Feed new insights back into learning tiers (memory cascade)
|
|
482
|
+
// This is the key integration — dream insights don't just sit in the journal,
|
|
483
|
+
// they propagate back into the pattern cache, solution index, and user profile.
|
|
484
|
+
if (newlyCreatedInsights.length > 0) {
|
|
485
|
+
result.applied = applyDreamInsights(newlyCreatedInsights);
|
|
486
|
+
}
|
|
309
487
|
// Save everything
|
|
310
488
|
saveJournal(journal);
|
|
311
489
|
state.cycles++;
|
package/dist/learning.d.ts
CHANGED
|
@@ -179,5 +179,11 @@ export declare function getExtendedStats(): LearningStats & {
|
|
|
179
179
|
projectsCount: number;
|
|
180
180
|
topKnowledge: string[];
|
|
181
181
|
};
|
|
182
|
+
/** Get the top N patterns from the pattern cache, ranked by effectiveness */
|
|
183
|
+
export declare function getTopPatterns(n?: number): CachedPattern[];
|
|
184
|
+
/** Get the top N solutions from the solution index, ranked by confidence and reuse */
|
|
185
|
+
export declare function getTopSolutions(n?: number): CachedSolution[];
|
|
186
|
+
/** Get a text summary of the user profile for consolidation prompts */
|
|
187
|
+
export declare function getProfileSummary(): string;
|
|
182
188
|
export {};
|
|
183
189
|
//# sourceMappingURL=learning.d.ts.map
|
package/dist/learning.js
CHANGED
|
@@ -878,4 +878,45 @@ export function getExtendedStats() {
|
|
|
878
878
|
.map(k => k.fact.slice(0, 80)),
|
|
879
879
|
};
|
|
880
880
|
}
|
|
881
|
+
// ═══ 12. MEMORY CASCADE ACCESSORS ══════════════════════════════
|
|
882
|
+
// Read-only accessors for the dream engine to pull data from all tiers.
|
|
883
|
+
/** Get the top N patterns from the pattern cache, ranked by effectiveness */
|
|
884
|
+
export function getTopPatterns(n = 10) {
|
|
885
|
+
return [...patterns]
|
|
886
|
+
.sort((a, b) => (b.hits * b.successRate) - (a.hits * a.successRate))
|
|
887
|
+
.slice(0, n);
|
|
888
|
+
}
|
|
889
|
+
/** Get the top N solutions from the solution index, ranked by confidence and reuse */
|
|
890
|
+
export function getTopSolutions(n = 5) {
|
|
891
|
+
return [...solutions]
|
|
892
|
+
.sort((a, b) => (b.confidence * (b.reuses + 1)) - (a.confidence * (a.reuses + 1)))
|
|
893
|
+
.slice(0, n);
|
|
894
|
+
}
|
|
895
|
+
/** Get a text summary of the user profile for consolidation prompts */
|
|
896
|
+
export function getProfileSummary() {
|
|
897
|
+
const parts = [];
|
|
898
|
+
if (profile.techStack.length > 0) {
|
|
899
|
+
parts.push(`Tech stack: ${profile.techStack.join(', ')}`);
|
|
900
|
+
}
|
|
901
|
+
if (profile.responseStyle !== 'auto') {
|
|
902
|
+
parts.push(`Response style: ${profile.responseStyle}`);
|
|
903
|
+
}
|
|
904
|
+
const topTasks = Object.entries(profile.taskPatterns)
|
|
905
|
+
.sort((a, b) => b[1] - a[1])
|
|
906
|
+
.slice(0, 5);
|
|
907
|
+
if (topTasks.length > 0) {
|
|
908
|
+
parts.push(`Task patterns: ${topTasks.map(([t, n]) => `${t}(${n}x)`).join(', ')}`);
|
|
909
|
+
}
|
|
910
|
+
const topAgents = Object.entries(profile.preferredAgents)
|
|
911
|
+
.sort((a, b) => b[1] - a[1])
|
|
912
|
+
.slice(0, 3);
|
|
913
|
+
if (topAgents.length > 0) {
|
|
914
|
+
parts.push(`Preferred agents: ${topAgents.map(([a, n]) => `${a}(${n}x)`).join(', ')}`);
|
|
915
|
+
}
|
|
916
|
+
parts.push(`Sessions: ${profile.sessions}, Messages: ${profile.totalMessages}`);
|
|
917
|
+
if (profile.tokensSaved > 0) {
|
|
918
|
+
parts.push(`Tokens saved by learning: ${profile.tokensSaved}`);
|
|
919
|
+
}
|
|
920
|
+
return parts.join('\n');
|
|
921
|
+
}
|
|
881
922
|
//# sourceMappingURL=learning.js.map
|
package/package.json
CHANGED