@soleri/core 2.5.0 → 2.7.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/brain/intelligence.d.ts +1 -0
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +160 -148
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/control/identity-manager.d.ts +3 -1
- package/dist/control/identity-manager.d.ts.map +1 -1
- package/dist/control/identity-manager.js +53 -51
- package/dist/control/identity-manager.js.map +1 -1
- package/dist/control/intent-router.d.ts +1 -0
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +41 -32
- package/dist/control/intent-router.js.map +1 -1
- package/dist/curator/curator.d.ts +1 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +58 -99
- package/dist/curator/curator.js.map +1 -1
- package/dist/facades/facade-factory.js +1 -1
- package/dist/facades/facade-factory.js.map +1 -1
- package/dist/governance/governance.d.ts +1 -0
- package/dist/governance/governance.d.ts.map +1 -1
- package/dist/governance/governance.js +51 -68
- package/dist/governance/governance.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/persistence/index.d.ts +2 -1
- package/dist/persistence/index.d.ts.map +1 -1
- package/dist/persistence/index.js +1 -0
- package/dist/persistence/index.js.map +1 -1
- package/dist/persistence/postgres-provider.d.ts +46 -0
- package/dist/persistence/postgres-provider.d.ts.map +1 -0
- package/dist/persistence/postgres-provider.js +115 -0
- package/dist/persistence/postgres-provider.js.map +1 -0
- package/dist/persistence/sqlite-provider.d.ts +5 -2
- package/dist/persistence/sqlite-provider.d.ts.map +1 -1
- package/dist/persistence/sqlite-provider.js +37 -1
- package/dist/persistence/sqlite-provider.js.map +1 -1
- package/dist/persistence/types.d.ts +23 -1
- package/dist/persistence/types.d.ts.map +1 -1
- package/dist/project/project-registry.d.ts +4 -4
- package/dist/project/project-registry.d.ts.map +1 -1
- package/dist/project/project-registry.js +32 -50
- package/dist/project/project-registry.js.map +1 -1
- package/dist/runtime/admin-extra-ops.d.ts +3 -3
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +33 -3
- package/dist/runtime/admin-extra-ops.js.map +1 -1
- package/dist/runtime/core-ops.d.ts +4 -4
- package/dist/runtime/core-ops.js +4 -4
- package/dist/runtime/runtime.js +1 -1
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +3 -2
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +40 -2
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/vault/vault.d.ts +21 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +99 -0
- package/dist/vault/vault.js.map +1 -1
- package/package.json +4 -2
- package/src/__tests__/admin-extra-ops.test.ts +2 -2
- package/src/__tests__/core-ops.test.ts +8 -2
- package/src/__tests__/persistence.test.ts +66 -0
- package/src/__tests__/postgres-provider.test.ts +58 -0
- package/src/__tests__/vault-extra-ops.test.ts +2 -2
- package/src/__tests__/vault.test.ts +184 -0
- package/src/brain/intelligence.ts +258 -307
- package/src/control/identity-manager.ts +77 -75
- package/src/control/intent-router.ts +55 -57
- package/src/curator/curator.ts +124 -145
- package/src/facades/facade-factory.ts +1 -1
- package/src/governance/governance.ts +90 -107
- package/src/index.ts +2 -0
- package/src/persistence/index.ts +2 -0
- package/src/persistence/postgres-provider.ts +157 -0
- package/src/persistence/sqlite-provider.ts +55 -2
- package/src/persistence/types.ts +31 -1
- package/src/project/project-registry.ts +69 -74
- package/src/runtime/admin-extra-ops.ts +36 -3
- package/src/runtime/core-ops.ts +4 -4
- package/src/runtime/runtime.ts +1 -1
- package/src/runtime/vault-extra-ops.ts +42 -2
- package/src/vault/vault.ts +118 -0
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
10
|
import type { Vault } from '../vault/vault.js';
|
|
11
11
|
import type { Brain } from './brain.js';
|
|
12
|
+
import type { PersistenceProvider } from '../persistence/types.js';
|
|
12
13
|
import type {
|
|
13
14
|
PatternStrength,
|
|
14
15
|
StrengthsQuery,
|
|
@@ -40,18 +41,19 @@ const EXTRACTION_HIGH_FEEDBACK_RATIO = 0.8;
|
|
|
40
41
|
export class BrainIntelligence {
|
|
41
42
|
private vault: Vault;
|
|
42
43
|
private brain: Brain;
|
|
44
|
+
private provider: PersistenceProvider;
|
|
43
45
|
|
|
44
46
|
constructor(vault: Vault, brain: Brain) {
|
|
45
47
|
this.vault = vault;
|
|
46
48
|
this.brain = brain;
|
|
49
|
+
this.provider = vault.getProvider();
|
|
47
50
|
this.initializeTables();
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
// ─── Table Initialization ─────────────────────────────────────────
|
|
51
54
|
|
|
52
55
|
private initializeTables(): void {
|
|
53
|
-
|
|
54
|
-
db.exec(`
|
|
56
|
+
this.provider.execSql(`
|
|
55
57
|
CREATE TABLE IF NOT EXISTS brain_strengths (
|
|
56
58
|
pattern TEXT NOT NULL,
|
|
57
59
|
domain TEXT NOT NULL,
|
|
@@ -117,20 +119,19 @@ export class BrainIntelligence {
|
|
|
117
119
|
// ─── Session Lifecycle ────────────────────────────────────────────
|
|
118
120
|
|
|
119
121
|
lifecycle(input: SessionLifecycleInput): BrainSession {
|
|
120
|
-
const db = this.vault.getDb();
|
|
121
|
-
|
|
122
122
|
if (input.action === 'start') {
|
|
123
123
|
const id = input.sessionId ?? randomUUID();
|
|
124
|
-
|
|
124
|
+
this.provider.run(
|
|
125
125
|
`INSERT INTO brain_sessions (id, domain, context, tools_used, files_modified, plan_id)
|
|
126
126
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
127
|
+
[
|
|
128
|
+
id,
|
|
129
|
+
input.domain ?? null,
|
|
130
|
+
input.context ?? null,
|
|
131
|
+
JSON.stringify(input.toolsUsed ?? []),
|
|
132
|
+
JSON.stringify(input.filesModified ?? []),
|
|
133
|
+
input.planId ?? null,
|
|
134
|
+
],
|
|
134
135
|
);
|
|
135
136
|
return this.getSession(id)!;
|
|
136
137
|
}
|
|
@@ -160,7 +161,7 @@ export class BrainIntelligence {
|
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
values.push(sessionId);
|
|
163
|
-
|
|
164
|
+
this.provider.run(`UPDATE brain_sessions SET ${updates.join(', ')} WHERE id = ?`, values);
|
|
164
165
|
|
|
165
166
|
// Auto-extract knowledge if session has enough signal
|
|
166
167
|
this.autoExtractIfReady(this.getSession(sessionId)!);
|
|
@@ -191,11 +192,7 @@ export class BrainIntelligence {
|
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
getSessionContext(limit = 10): SessionContext {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
const rows = db
|
|
197
|
-
.prepare('SELECT * FROM brain_sessions ORDER BY started_at DESC LIMIT ?')
|
|
198
|
-
.all(limit) as Array<{
|
|
195
|
+
const rows = this.provider.all<{
|
|
199
196
|
id: string;
|
|
200
197
|
started_at: string;
|
|
201
198
|
ended_at: string | null;
|
|
@@ -206,7 +203,7 @@ export class BrainIntelligence {
|
|
|
206
203
|
plan_id: string | null;
|
|
207
204
|
plan_outcome: string | null;
|
|
208
205
|
extracted_at: string | null;
|
|
209
|
-
}
|
|
206
|
+
}>('SELECT * FROM brain_sessions ORDER BY started_at DESC LIMIT ?', [limit]);
|
|
210
207
|
|
|
211
208
|
const sessions = rows.map((r) => this.rowToSession(r));
|
|
212
209
|
|
|
@@ -234,36 +231,20 @@ export class BrainIntelligence {
|
|
|
234
231
|
}
|
|
235
232
|
|
|
236
233
|
archiveSessions(olderThanDays = 30): { archived: number } {
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
)
|
|
244
|
-
.run(olderThanDays);
|
|
234
|
+
const result = this.provider.run(
|
|
235
|
+
`DELETE FROM brain_sessions
|
|
236
|
+
WHERE ended_at IS NOT NULL
|
|
237
|
+
AND started_at < datetime('now', '-' || ? || ' days')`,
|
|
238
|
+
[olderThanDays],
|
|
239
|
+
);
|
|
245
240
|
return { archived: result.changes };
|
|
246
241
|
}
|
|
247
242
|
|
|
248
243
|
// ─── Strength Scoring ─────────────────────────────────────────────
|
|
249
244
|
|
|
250
245
|
computeStrengths(): PatternStrength[] {
|
|
251
|
-
const db = this.vault.getDb();
|
|
252
|
-
|
|
253
246
|
// Gather feedback data grouped by entry_id
|
|
254
|
-
const feedbackRows =
|
|
255
|
-
.prepare(
|
|
256
|
-
`SELECT entry_id,
|
|
257
|
-
COUNT(*) as total,
|
|
258
|
-
SUM(CASE WHEN action = 'accepted' THEN 1 ELSE 0 END) as accepted,
|
|
259
|
-
SUM(CASE WHEN action = 'dismissed' THEN 1 ELSE 0 END) as dismissed,
|
|
260
|
-
SUM(CASE WHEN action = 'modified' THEN 1 ELSE 0 END) as modified,
|
|
261
|
-
SUM(CASE WHEN action = 'failed' THEN 1 ELSE 0 END) as failed,
|
|
262
|
-
MAX(created_at) as last_used
|
|
263
|
-
FROM brain_feedback
|
|
264
|
-
GROUP BY entry_id`,
|
|
265
|
-
)
|
|
266
|
-
.all() as Array<{
|
|
247
|
+
const feedbackRows = this.provider.all<{
|
|
267
248
|
entry_id: string;
|
|
268
249
|
total: number;
|
|
269
250
|
accepted: number;
|
|
@@ -271,12 +252,22 @@ export class BrainIntelligence {
|
|
|
271
252
|
modified: number;
|
|
272
253
|
failed: number;
|
|
273
254
|
last_used: string;
|
|
274
|
-
}
|
|
255
|
+
}>(
|
|
256
|
+
`SELECT entry_id,
|
|
257
|
+
COUNT(*) as total,
|
|
258
|
+
SUM(CASE WHEN action = 'accepted' THEN 1 ELSE 0 END) as accepted,
|
|
259
|
+
SUM(CASE WHEN action = 'dismissed' THEN 1 ELSE 0 END) as dismissed,
|
|
260
|
+
SUM(CASE WHEN action = 'modified' THEN 1 ELSE 0 END) as modified,
|
|
261
|
+
SUM(CASE WHEN action = 'failed' THEN 1 ELSE 0 END) as failed,
|
|
262
|
+
MAX(created_at) as last_used
|
|
263
|
+
FROM brain_feedback
|
|
264
|
+
GROUP BY entry_id`,
|
|
265
|
+
);
|
|
275
266
|
|
|
276
267
|
// Count unique session domains as spread proxy
|
|
277
|
-
const sessionRows =
|
|
278
|
-
|
|
279
|
-
|
|
268
|
+
const sessionRows = this.provider.all<{ domain: string }>(
|
|
269
|
+
'SELECT DISTINCT domain FROM brain_sessions WHERE domain IS NOT NULL',
|
|
270
|
+
);
|
|
280
271
|
const uniqueDomains = new Set(sessionRows.map((r) => r.domain));
|
|
281
272
|
|
|
282
273
|
const now = Date.now();
|
|
@@ -326,23 +317,24 @@ export class BrainIntelligence {
|
|
|
326
317
|
strengths.push(ps);
|
|
327
318
|
|
|
328
319
|
// Persist
|
|
329
|
-
|
|
320
|
+
this.provider.run(
|
|
330
321
|
`INSERT OR REPLACE INTO brain_strengths
|
|
331
322
|
(pattern, domain, strength, usage_score, spread_score, success_score, recency_score,
|
|
332
323
|
usage_count, unique_contexts, success_rate, last_used, updated_at)
|
|
333
324
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
325
|
+
[
|
|
326
|
+
ps.pattern,
|
|
327
|
+
ps.domain,
|
|
328
|
+
ps.strength,
|
|
329
|
+
ps.usageScore,
|
|
330
|
+
ps.spreadScore,
|
|
331
|
+
ps.successScore,
|
|
332
|
+
ps.recencyScore,
|
|
333
|
+
ps.usageCount,
|
|
334
|
+
ps.uniqueContexts,
|
|
335
|
+
ps.successRate,
|
|
336
|
+
ps.lastUsed,
|
|
337
|
+
],
|
|
346
338
|
);
|
|
347
339
|
}
|
|
348
340
|
|
|
@@ -350,7 +342,6 @@ export class BrainIntelligence {
|
|
|
350
342
|
}
|
|
351
343
|
|
|
352
344
|
getStrengths(query?: StrengthsQuery): PatternStrength[] {
|
|
353
|
-
const db = this.vault.getDb();
|
|
354
345
|
const conditions: string[] = [];
|
|
355
346
|
const values: unknown[] = [];
|
|
356
347
|
|
|
@@ -367,9 +358,7 @@ export class BrainIntelligence {
|
|
|
367
358
|
const limit = query?.limit ?? 50;
|
|
368
359
|
values.push(limit);
|
|
369
360
|
|
|
370
|
-
const rows =
|
|
371
|
-
.prepare(`SELECT * FROM brain_strengths ${where} ORDER BY strength DESC LIMIT ?`)
|
|
372
|
-
.all(...values) as Array<{
|
|
361
|
+
const rows = this.provider.all<{
|
|
373
362
|
pattern: string;
|
|
374
363
|
domain: string;
|
|
375
364
|
strength: number;
|
|
@@ -381,7 +370,7 @@ export class BrainIntelligence {
|
|
|
381
370
|
unique_contexts: number;
|
|
382
371
|
success_rate: number;
|
|
383
372
|
last_used: string;
|
|
384
|
-
}
|
|
373
|
+
}>(`SELECT * FROM brain_strengths ${where} ORDER BY strength DESC LIMIT ?`, values);
|
|
385
374
|
|
|
386
375
|
return rows.map((r) => ({
|
|
387
376
|
pattern: r.pattern,
|
|
@@ -426,18 +415,20 @@ export class BrainIntelligence {
|
|
|
426
415
|
|
|
427
416
|
// Boost patterns with high source-specific acceptance rates
|
|
428
417
|
if (context.source) {
|
|
429
|
-
const db = this.vault.getDb();
|
|
430
418
|
for (const s of strengths) {
|
|
431
|
-
const row =
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
419
|
+
const row = this.provider.get<{
|
|
420
|
+
total: number;
|
|
421
|
+
accepted: number;
|
|
422
|
+
modified: number;
|
|
423
|
+
}>(
|
|
424
|
+
`SELECT COUNT(*) as total,
|
|
425
|
+
SUM(CASE WHEN action = 'accepted' THEN 1 ELSE 0 END) as accepted,
|
|
426
|
+
SUM(CASE WHEN action = 'modified' THEN 1 ELSE 0 END) as modified
|
|
427
|
+
FROM brain_feedback
|
|
428
|
+
WHERE entry_id = (SELECT id FROM entries WHERE title = ? LIMIT 1)
|
|
429
|
+
AND source = ?`,
|
|
430
|
+
[s.pattern, context.source],
|
|
431
|
+
) as {
|
|
441
432
|
total: number;
|
|
442
433
|
accepted: number;
|
|
443
434
|
modified: number;
|
|
@@ -463,7 +454,6 @@ export class BrainIntelligence {
|
|
|
463
454
|
|
|
464
455
|
const proposals: KnowledgeProposal[] = [];
|
|
465
456
|
const rulesApplied: string[] = [];
|
|
466
|
-
const db = this.vault.getDb();
|
|
467
457
|
|
|
468
458
|
// Rule 1: Repeated tool usage (3+ same tool)
|
|
469
459
|
const toolCounts = new Map<string, number>();
|
|
@@ -474,7 +464,7 @@ export class BrainIntelligence {
|
|
|
474
464
|
if (count >= EXTRACTION_TOOL_THRESHOLD) {
|
|
475
465
|
rulesApplied.push('repeated_tool_usage');
|
|
476
466
|
proposals.push(
|
|
477
|
-
this.createProposal(
|
|
467
|
+
this.createProposal(sessionId, 'repeated_tool_usage', 'pattern', {
|
|
478
468
|
title: `Frequent use of ${tool}`,
|
|
479
469
|
description: `Tool ${tool} was used ${count} times in session. Consider automating or abstracting this workflow.`,
|
|
480
470
|
confidence: Math.min(0.9, 0.5 + count * 0.1),
|
|
@@ -487,7 +477,7 @@ export class BrainIntelligence {
|
|
|
487
477
|
if (session.filesModified.length >= EXTRACTION_FILE_THRESHOLD) {
|
|
488
478
|
rulesApplied.push('multi_file_edit');
|
|
489
479
|
proposals.push(
|
|
490
|
-
this.createProposal(
|
|
480
|
+
this.createProposal(sessionId, 'multi_file_edit', 'pattern', {
|
|
491
481
|
title: `Multi-file change pattern (${session.filesModified.length} files)`,
|
|
492
482
|
description: `Session modified ${session.filesModified.length} files: ${session.filesModified.slice(0, 5).join(', ')}${session.filesModified.length > 5 ? '...' : ''}. This may indicate an architectural pattern.`,
|
|
493
483
|
confidence: Math.min(0.8, 0.4 + session.filesModified.length * 0.05),
|
|
@@ -503,7 +493,7 @@ export class BrainIntelligence {
|
|
|
503
493
|
if (durationMin > EXTRACTION_LONG_SESSION_MINUTES) {
|
|
504
494
|
rulesApplied.push('long_session');
|
|
505
495
|
proposals.push(
|
|
506
|
-
this.createProposal(
|
|
496
|
+
this.createProposal(sessionId, 'long_session', 'anti-pattern', {
|
|
507
497
|
title: `Long session (${Math.round(durationMin)} minutes)`,
|
|
508
498
|
description: `Session lasted ${Math.round(durationMin)} minutes. Consider breaking complex tasks into smaller steps or improving automation.`,
|
|
509
499
|
confidence: 0.5,
|
|
@@ -516,7 +506,7 @@ export class BrainIntelligence {
|
|
|
516
506
|
if (session.planId && session.planOutcome === 'completed') {
|
|
517
507
|
rulesApplied.push('plan_completed');
|
|
518
508
|
proposals.push(
|
|
519
|
-
this.createProposal(
|
|
509
|
+
this.createProposal(sessionId, 'plan_completed', 'workflow', {
|
|
520
510
|
title: `Successful plan: ${session.planId}`,
|
|
521
511
|
description: `Plan ${session.planId} completed successfully. This workflow can be reused for similar tasks.`,
|
|
522
512
|
confidence: 0.8,
|
|
@@ -528,7 +518,7 @@ export class BrainIntelligence {
|
|
|
528
518
|
if (session.planId && session.planOutcome === 'abandoned') {
|
|
529
519
|
rulesApplied.push('plan_abandoned');
|
|
530
520
|
proposals.push(
|
|
531
|
-
this.createProposal(
|
|
521
|
+
this.createProposal(sessionId, 'plan_abandoned', 'anti-pattern', {
|
|
532
522
|
title: `Abandoned plan: ${session.planId}`,
|
|
533
523
|
description: `Plan ${session.planId} was abandoned. Review what went wrong to avoid repeating in future sessions.`,
|
|
534
524
|
confidence: 0.7,
|
|
@@ -537,15 +527,18 @@ export class BrainIntelligence {
|
|
|
537
527
|
}
|
|
538
528
|
|
|
539
529
|
// Rule 6: High feedback ratio (>80% accept or dismiss)
|
|
540
|
-
const feedbackRow =
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
530
|
+
const feedbackRow = this.provider.get<{
|
|
531
|
+
total: number;
|
|
532
|
+
accepted: number;
|
|
533
|
+
dismissed: number;
|
|
534
|
+
}>(
|
|
535
|
+
`SELECT COUNT(*) as total,
|
|
536
|
+
SUM(CASE WHEN action = 'accepted' THEN 1 ELSE 0 END) as accepted,
|
|
537
|
+
SUM(CASE WHEN action = 'dismissed' THEN 1 ELSE 0 END) as dismissed
|
|
538
|
+
FROM brain_feedback
|
|
539
|
+
WHERE created_at >= ? AND created_at <= ?`,
|
|
540
|
+
[session.startedAt, session.endedAt ?? new Date().toISOString()],
|
|
541
|
+
) as {
|
|
549
542
|
total: number;
|
|
550
543
|
accepted: number;
|
|
551
544
|
dismissed: number;
|
|
@@ -558,7 +551,7 @@ export class BrainIntelligence {
|
|
|
558
551
|
if (acceptRate >= EXTRACTION_HIGH_FEEDBACK_RATIO) {
|
|
559
552
|
rulesApplied.push('high_accept_ratio');
|
|
560
553
|
proposals.push(
|
|
561
|
-
this.createProposal(
|
|
554
|
+
this.createProposal(sessionId, 'high_accept_ratio', 'pattern', {
|
|
562
555
|
title: `High search acceptance rate (${Math.round(acceptRate * 100)}%)`,
|
|
563
556
|
description: `Search results were accepted ${Math.round(acceptRate * 100)}% of the time. Brain scoring is well-calibrated for this type of work.`,
|
|
564
557
|
confidence: 0.7,
|
|
@@ -567,7 +560,7 @@ export class BrainIntelligence {
|
|
|
567
560
|
} else if (dismissRate >= EXTRACTION_HIGH_FEEDBACK_RATIO) {
|
|
568
561
|
rulesApplied.push('high_dismiss_ratio');
|
|
569
562
|
proposals.push(
|
|
570
|
-
this.createProposal(
|
|
563
|
+
this.createProposal(sessionId, 'high_dismiss_ratio', 'anti-pattern', {
|
|
571
564
|
title: `High search dismissal rate (${Math.round(dismissRate * 100)}%)`,
|
|
572
565
|
description: `Search results were dismissed ${Math.round(dismissRate * 100)}% of the time. Brain scoring may need recalibration for this domain.`,
|
|
573
566
|
confidence: 0.7,
|
|
@@ -577,9 +570,9 @@ export class BrainIntelligence {
|
|
|
577
570
|
}
|
|
578
571
|
|
|
579
572
|
// Mark session as extracted
|
|
580
|
-
|
|
573
|
+
this.provider.run("UPDATE brain_sessions SET extracted_at = datetime('now') WHERE id = ?", [
|
|
581
574
|
sessionId,
|
|
582
|
-
);
|
|
575
|
+
]);
|
|
583
576
|
|
|
584
577
|
return {
|
|
585
578
|
sessionId,
|
|
@@ -591,28 +584,26 @@ export class BrainIntelligence {
|
|
|
591
584
|
resetExtracted(options?: { sessionId?: string; since?: string; all?: boolean }): {
|
|
592
585
|
reset: number;
|
|
593
586
|
} {
|
|
594
|
-
const db = this.vault.getDb();
|
|
595
|
-
|
|
596
587
|
if (options?.sessionId) {
|
|
597
|
-
const info =
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
.run(options.sessionId);
|
|
588
|
+
const info = this.provider.run(
|
|
589
|
+
'UPDATE brain_sessions SET extracted_at = NULL WHERE id = ? AND extracted_at IS NOT NULL',
|
|
590
|
+
[options.sessionId],
|
|
591
|
+
);
|
|
602
592
|
return { reset: info.changes };
|
|
603
593
|
}
|
|
604
594
|
|
|
605
595
|
if (options?.since) {
|
|
606
|
-
const info =
|
|
607
|
-
|
|
608
|
-
|
|
596
|
+
const info = this.provider.run(
|
|
597
|
+
'UPDATE brain_sessions SET extracted_at = NULL WHERE extracted_at >= ?',
|
|
598
|
+
[options.since],
|
|
599
|
+
);
|
|
609
600
|
return { reset: info.changes };
|
|
610
601
|
}
|
|
611
602
|
|
|
612
603
|
if (options?.all) {
|
|
613
|
-
const info =
|
|
614
|
-
|
|
615
|
-
|
|
604
|
+
const info = this.provider.run(
|
|
605
|
+
'UPDATE brain_sessions SET extracted_at = NULL WHERE extracted_at IS NOT NULL',
|
|
606
|
+
);
|
|
616
607
|
return { reset: info.changes };
|
|
617
608
|
}
|
|
618
609
|
|
|
@@ -624,7 +615,6 @@ export class BrainIntelligence {
|
|
|
624
615
|
promoted?: boolean;
|
|
625
616
|
limit?: number;
|
|
626
617
|
}): KnowledgeProposal[] {
|
|
627
|
-
const db = this.vault.getDb();
|
|
628
618
|
const conditions: string[] = [];
|
|
629
619
|
const values: unknown[] = [];
|
|
630
620
|
|
|
@@ -641,9 +631,7 @@ export class BrainIntelligence {
|
|
|
641
631
|
const limit = options?.limit ?? 50;
|
|
642
632
|
values.push(limit);
|
|
643
633
|
|
|
644
|
-
const rows =
|
|
645
|
-
.prepare(`SELECT * FROM brain_proposals ${where} ORDER BY created_at DESC LIMIT ?`)
|
|
646
|
-
.all(...values) as Array<{
|
|
634
|
+
const rows = this.provider.all<{
|
|
647
635
|
id: string;
|
|
648
636
|
session_id: string;
|
|
649
637
|
rule: string;
|
|
@@ -653,7 +641,7 @@ export class BrainIntelligence {
|
|
|
653
641
|
confidence: number;
|
|
654
642
|
promoted: number;
|
|
655
643
|
created_at: string;
|
|
656
|
-
}
|
|
644
|
+
}>(`SELECT * FROM brain_proposals ${where} ORDER BY created_at DESC LIMIT ?`, values);
|
|
657
645
|
|
|
658
646
|
return rows.map((r) => this.rowToProposal(r));
|
|
659
647
|
}
|
|
@@ -683,26 +671,23 @@ export class BrainIntelligence {
|
|
|
683
671
|
failed: string[];
|
|
684
672
|
gated: Array<{ id: string; action: string; reason?: string }>;
|
|
685
673
|
} {
|
|
686
|
-
const db = this.vault.getDb();
|
|
687
674
|
let promoted = 0;
|
|
688
675
|
const failed: string[] = [];
|
|
689
676
|
const gated: Array<{ id: string; action: string; reason?: string }> = [];
|
|
690
677
|
const pp = projectPath ?? '.';
|
|
691
678
|
|
|
692
679
|
for (const id of proposalIds) {
|
|
693
|
-
const row =
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
}
|
|
705
|
-
| undefined;
|
|
680
|
+
const row = this.provider.get<{
|
|
681
|
+
id: string;
|
|
682
|
+
session_id: string;
|
|
683
|
+
rule: string;
|
|
684
|
+
type: string;
|
|
685
|
+
title: string;
|
|
686
|
+
description: string;
|
|
687
|
+
confidence: number;
|
|
688
|
+
promoted: number;
|
|
689
|
+
created_at: string;
|
|
690
|
+
}>('SELECT * FROM brain_proposals WHERE id = ?', [id]);
|
|
706
691
|
|
|
707
692
|
if (!row) {
|
|
708
693
|
failed.push(id);
|
|
@@ -761,7 +746,7 @@ export class BrainIntelligence {
|
|
|
761
746
|
tags: ['auto-extracted', row.rule],
|
|
762
747
|
});
|
|
763
748
|
|
|
764
|
-
|
|
749
|
+
this.provider.run('UPDATE brain_proposals SET promoted = 1 WHERE id = ?', [id]);
|
|
765
750
|
promoted++;
|
|
766
751
|
}
|
|
767
752
|
|
|
@@ -788,16 +773,13 @@ export class BrainIntelligence {
|
|
|
788
773
|
}
|
|
789
774
|
|
|
790
775
|
getGlobalPatterns(limit = 20): GlobalPattern[] {
|
|
791
|
-
const
|
|
792
|
-
const rows = db
|
|
793
|
-
.prepare('SELECT * FROM brain_global_registry ORDER BY total_strength DESC LIMIT ?')
|
|
794
|
-
.all(limit) as Array<{
|
|
776
|
+
const rows = this.provider.all<{
|
|
795
777
|
pattern: string;
|
|
796
778
|
domains: string;
|
|
797
779
|
total_strength: number;
|
|
798
780
|
avg_strength: number;
|
|
799
781
|
domain_count: number;
|
|
800
|
-
}
|
|
782
|
+
}>('SELECT * FROM brain_global_registry ORDER BY total_strength DESC LIMIT ?', [limit]);
|
|
801
783
|
|
|
802
784
|
return rows.map((r) => ({
|
|
803
785
|
pattern: r.pattern,
|
|
@@ -809,16 +791,13 @@ export class BrainIntelligence {
|
|
|
809
791
|
}
|
|
810
792
|
|
|
811
793
|
getDomainProfile(domain: string): DomainProfile | null {
|
|
812
|
-
const
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
last_activity: string;
|
|
820
|
-
}
|
|
821
|
-
| undefined;
|
|
794
|
+
const row = this.provider.get<{
|
|
795
|
+
domain: string;
|
|
796
|
+
top_patterns: string;
|
|
797
|
+
session_count: number;
|
|
798
|
+
avg_session_duration: number;
|
|
799
|
+
last_activity: string;
|
|
800
|
+
}>('SELECT * FROM brain_domain_profiles WHERE domain = ?', [domain]);
|
|
822
801
|
|
|
823
802
|
if (!row) return null;
|
|
824
803
|
|
|
@@ -834,32 +813,27 @@ export class BrainIntelligence {
|
|
|
834
813
|
// ─── Data Management ──────────────────────────────────────────────
|
|
835
814
|
|
|
836
815
|
getStats(): BrainIntelligenceStats {
|
|
837
|
-
const
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
)
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
)
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
db.prepare('SELECT COUNT(*) as c FROM brain_global_registry').get() as { c: number }
|
|
859
|
-
).c;
|
|
860
|
-
const domainProfiles = (
|
|
861
|
-
db.prepare('SELECT COUNT(*) as c FROM brain_domain_profiles').get() as { c: number }
|
|
862
|
-
).c;
|
|
816
|
+
const strengths = this.provider.get<{ c: number }>(
|
|
817
|
+
'SELECT COUNT(*) as c FROM brain_strengths',
|
|
818
|
+
)!.c;
|
|
819
|
+
const sessions = this.provider.get<{ c: number }>(
|
|
820
|
+
'SELECT COUNT(*) as c FROM brain_sessions',
|
|
821
|
+
)!.c;
|
|
822
|
+
const activeSessions = this.provider.get<{ c: number }>(
|
|
823
|
+
'SELECT COUNT(*) as c FROM brain_sessions WHERE ended_at IS NULL',
|
|
824
|
+
)!.c;
|
|
825
|
+
const proposals = this.provider.get<{ c: number }>(
|
|
826
|
+
'SELECT COUNT(*) as c FROM brain_proposals',
|
|
827
|
+
)!.c;
|
|
828
|
+
const promotedProposals = this.provider.get<{ c: number }>(
|
|
829
|
+
'SELECT COUNT(*) as c FROM brain_proposals WHERE promoted = 1',
|
|
830
|
+
)!.c;
|
|
831
|
+
const globalPatterns = this.provider.get<{ c: number }>(
|
|
832
|
+
'SELECT COUNT(*) as c FROM brain_global_registry',
|
|
833
|
+
)!.c;
|
|
834
|
+
const domainProfiles = this.provider.get<{ c: number }>(
|
|
835
|
+
'SELECT COUNT(*) as c FROM brain_domain_profiles',
|
|
836
|
+
)!.c;
|
|
863
837
|
|
|
864
838
|
return {
|
|
865
839
|
strengths,
|
|
@@ -873,13 +847,9 @@ export class BrainIntelligence {
|
|
|
873
847
|
}
|
|
874
848
|
|
|
875
849
|
exportData(): BrainExportData {
|
|
876
|
-
const db = this.vault.getDb();
|
|
877
|
-
|
|
878
850
|
const strengths = this.getStrengths({ limit: 10000 });
|
|
879
851
|
|
|
880
|
-
const sessionRows =
|
|
881
|
-
.prepare('SELECT * FROM brain_sessions ORDER BY started_at DESC')
|
|
882
|
-
.all() as Array<{
|
|
852
|
+
const sessionRows = this.provider.all<{
|
|
883
853
|
id: string;
|
|
884
854
|
started_at: string;
|
|
885
855
|
ended_at: string | null;
|
|
@@ -890,19 +860,19 @@ export class BrainIntelligence {
|
|
|
890
860
|
plan_id: string | null;
|
|
891
861
|
plan_outcome: string | null;
|
|
892
862
|
extracted_at: string | null;
|
|
893
|
-
}
|
|
863
|
+
}>('SELECT * FROM brain_sessions ORDER BY started_at DESC');
|
|
894
864
|
const sessions = sessionRows.map((r) => this.rowToSession(r));
|
|
895
865
|
|
|
896
866
|
const proposals = this.getProposals({ limit: 10000 });
|
|
897
867
|
const globalPatterns = this.getGlobalPatterns(10000);
|
|
898
868
|
|
|
899
|
-
const profileRows =
|
|
869
|
+
const profileRows = this.provider.all<{
|
|
900
870
|
domain: string;
|
|
901
871
|
top_patterns: string;
|
|
902
872
|
session_count: number;
|
|
903
873
|
avg_session_duration: number;
|
|
904
874
|
last_activity: string;
|
|
905
|
-
}
|
|
875
|
+
}>('SELECT * FROM brain_domain_profiles');
|
|
906
876
|
const domainProfiles = profileRows.map((r) => ({
|
|
907
877
|
domain: r.domain,
|
|
908
878
|
topPatterns: JSON.parse(r.top_patterns) as Array<{ pattern: string; strength: number }>,
|
|
@@ -922,136 +892,125 @@ export class BrainIntelligence {
|
|
|
922
892
|
}
|
|
923
893
|
|
|
924
894
|
importData(data: BrainExportData): BrainImportResult {
|
|
925
|
-
const db = this.vault.getDb();
|
|
926
895
|
const result: BrainImportResult = {
|
|
927
896
|
imported: { strengths: 0, sessions: 0, proposals: 0, globalPatterns: 0, domainProfiles: 0 },
|
|
928
897
|
};
|
|
929
898
|
|
|
930
|
-
|
|
899
|
+
this.provider.transaction(() => {
|
|
931
900
|
// Import strengths
|
|
932
|
-
const insertStrength = db.prepare(
|
|
933
|
-
`INSERT OR REPLACE INTO brain_strengths
|
|
934
|
-
(pattern, domain, strength, usage_score, spread_score, success_score, recency_score,
|
|
935
|
-
usage_count, unique_contexts, success_rate, last_used, updated_at)
|
|
936
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
|
|
937
|
-
);
|
|
938
901
|
for (const s of data.strengths) {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
902
|
+
this.provider.run(
|
|
903
|
+
`INSERT OR REPLACE INTO brain_strengths
|
|
904
|
+
(pattern, domain, strength, usage_score, spread_score, success_score, recency_score,
|
|
905
|
+
usage_count, unique_contexts, success_rate, last_used, updated_at)
|
|
906
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
|
|
907
|
+
[
|
|
908
|
+
s.pattern,
|
|
909
|
+
s.domain,
|
|
910
|
+
s.strength,
|
|
911
|
+
s.usageScore,
|
|
912
|
+
s.spreadScore,
|
|
913
|
+
s.successScore,
|
|
914
|
+
s.recencyScore,
|
|
915
|
+
s.usageCount,
|
|
916
|
+
s.uniqueContexts,
|
|
917
|
+
s.successRate,
|
|
918
|
+
s.lastUsed,
|
|
919
|
+
],
|
|
951
920
|
);
|
|
952
921
|
result.imported.strengths++;
|
|
953
922
|
}
|
|
954
923
|
|
|
955
924
|
// Import sessions
|
|
956
|
-
const insertSession = db.prepare(
|
|
957
|
-
`INSERT OR IGNORE INTO brain_sessions
|
|
958
|
-
(id, started_at, ended_at, domain, context, tools_used, files_modified, plan_id, plan_outcome, extracted_at)
|
|
959
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
960
|
-
);
|
|
961
925
|
for (const s of data.sessions) {
|
|
962
|
-
const changes =
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
926
|
+
const changes = this.provider.run(
|
|
927
|
+
`INSERT OR IGNORE INTO brain_sessions
|
|
928
|
+
(id, started_at, ended_at, domain, context, tools_used, files_modified, plan_id, plan_outcome, extracted_at)
|
|
929
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
930
|
+
[
|
|
931
|
+
s.id,
|
|
932
|
+
s.startedAt,
|
|
933
|
+
s.endedAt,
|
|
934
|
+
s.domain,
|
|
935
|
+
s.context,
|
|
936
|
+
JSON.stringify(s.toolsUsed),
|
|
937
|
+
JSON.stringify(s.filesModified),
|
|
938
|
+
s.planId,
|
|
939
|
+
s.planOutcome,
|
|
940
|
+
s.extractedAt ?? null,
|
|
941
|
+
],
|
|
973
942
|
);
|
|
974
943
|
if (changes.changes > 0) result.imported.sessions++;
|
|
975
944
|
}
|
|
976
945
|
|
|
977
946
|
// Import proposals
|
|
978
|
-
const insertProposal = db.prepare(
|
|
979
|
-
`INSERT OR IGNORE INTO brain_proposals
|
|
980
|
-
(id, session_id, rule, type, title, description, confidence, promoted, created_at)
|
|
981
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
982
|
-
);
|
|
983
947
|
for (const p of data.proposals) {
|
|
984
|
-
const changes =
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
948
|
+
const changes = this.provider.run(
|
|
949
|
+
`INSERT OR IGNORE INTO brain_proposals
|
|
950
|
+
(id, session_id, rule, type, title, description, confidence, promoted, created_at)
|
|
951
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
952
|
+
[
|
|
953
|
+
p.id,
|
|
954
|
+
p.sessionId,
|
|
955
|
+
p.rule,
|
|
956
|
+
p.type,
|
|
957
|
+
p.title,
|
|
958
|
+
p.description,
|
|
959
|
+
p.confidence,
|
|
960
|
+
p.promoted ? 1 : 0,
|
|
961
|
+
p.createdAt,
|
|
962
|
+
],
|
|
994
963
|
);
|
|
995
964
|
if (changes.changes > 0) result.imported.proposals++;
|
|
996
965
|
}
|
|
997
966
|
|
|
998
967
|
// Import global patterns
|
|
999
|
-
const insertGlobal = db.prepare(
|
|
1000
|
-
`INSERT OR REPLACE INTO brain_global_registry
|
|
1001
|
-
(pattern, domains, total_strength, avg_strength, domain_count, updated_at)
|
|
1002
|
-
VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
|
1003
|
-
);
|
|
1004
968
|
for (const g of data.globalPatterns) {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
g.avgStrength,
|
|
1010
|
-
g.domainCount,
|
|
969
|
+
this.provider.run(
|
|
970
|
+
`INSERT OR REPLACE INTO brain_global_registry
|
|
971
|
+
(pattern, domains, total_strength, avg_strength, domain_count, updated_at)
|
|
972
|
+
VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
|
973
|
+
[g.pattern, JSON.stringify(g.domains), g.totalStrength, g.avgStrength, g.domainCount],
|
|
1011
974
|
);
|
|
1012
975
|
result.imported.globalPatterns++;
|
|
1013
976
|
}
|
|
1014
977
|
|
|
1015
978
|
// Import domain profiles
|
|
1016
|
-
const insertProfile = db.prepare(
|
|
1017
|
-
`INSERT OR REPLACE INTO brain_domain_profiles
|
|
1018
|
-
(domain, top_patterns, session_count, avg_session_duration, last_activity, updated_at)
|
|
1019
|
-
VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
|
1020
|
-
);
|
|
1021
979
|
for (const d of data.domainProfiles) {
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
980
|
+
this.provider.run(
|
|
981
|
+
`INSERT OR REPLACE INTO brain_domain_profiles
|
|
982
|
+
(domain, top_patterns, session_count, avg_session_duration, last_activity, updated_at)
|
|
983
|
+
VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
|
984
|
+
[
|
|
985
|
+
d.domain,
|
|
986
|
+
JSON.stringify(d.topPatterns),
|
|
987
|
+
d.sessionCount,
|
|
988
|
+
d.avgSessionDuration,
|
|
989
|
+
d.lastActivity,
|
|
990
|
+
],
|
|
1028
991
|
);
|
|
1029
992
|
result.imported.domainProfiles++;
|
|
1030
993
|
}
|
|
1031
994
|
});
|
|
1032
995
|
|
|
1033
|
-
tx();
|
|
1034
996
|
return result;
|
|
1035
997
|
}
|
|
1036
998
|
|
|
1037
999
|
// ─── Private Helpers ──────────────────────────────────────────────
|
|
1038
1000
|
|
|
1039
1001
|
private getSession(id: string): BrainSession | null {
|
|
1040
|
-
const
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
extracted_at: string | null;
|
|
1053
|
-
}
|
|
1054
|
-
| undefined;
|
|
1002
|
+
const row = this.provider.get<{
|
|
1003
|
+
id: string;
|
|
1004
|
+
started_at: string;
|
|
1005
|
+
ended_at: string | null;
|
|
1006
|
+
domain: string | null;
|
|
1007
|
+
context: string | null;
|
|
1008
|
+
tools_used: string;
|
|
1009
|
+
files_modified: string;
|
|
1010
|
+
plan_id: string | null;
|
|
1011
|
+
plan_outcome: string | null;
|
|
1012
|
+
extracted_at: string | null;
|
|
1013
|
+
}>('SELECT * FROM brain_sessions WHERE id = ?', [id]);
|
|
1055
1014
|
|
|
1056
1015
|
if (!row) return null;
|
|
1057
1016
|
return this.rowToSession(row);
|
|
@@ -1108,17 +1067,17 @@ export class BrainIntelligence {
|
|
|
1108
1067
|
}
|
|
1109
1068
|
|
|
1110
1069
|
private createProposal(
|
|
1111
|
-
db: ReturnType<Vault['getDb']>,
|
|
1112
1070
|
sessionId: string,
|
|
1113
1071
|
rule: string,
|
|
1114
1072
|
type: 'pattern' | 'anti-pattern' | 'workflow',
|
|
1115
1073
|
data: { title: string; description: string; confidence: number },
|
|
1116
1074
|
): KnowledgeProposal {
|
|
1117
1075
|
const id = randomUUID();
|
|
1118
|
-
|
|
1076
|
+
this.provider.run(
|
|
1119
1077
|
`INSERT INTO brain_proposals (id, session_id, rule, type, title, description, confidence)
|
|
1120
1078
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
1121
|
-
|
|
1079
|
+
[id, sessionId, rule, type, data.title, data.description, data.confidence],
|
|
1080
|
+
);
|
|
1122
1081
|
|
|
1123
1082
|
return {
|
|
1124
1083
|
id,
|
|
@@ -1134,8 +1093,6 @@ export class BrainIntelligence {
|
|
|
1134
1093
|
}
|
|
1135
1094
|
|
|
1136
1095
|
private buildGlobalRegistry(strengths: PatternStrength[]): number {
|
|
1137
|
-
const db = this.vault.getDb();
|
|
1138
|
-
|
|
1139
1096
|
// Group strengths by pattern
|
|
1140
1097
|
const patternMap = new Map<string, PatternStrength[]>();
|
|
1141
1098
|
for (const s of strengths) {
|
|
@@ -1144,13 +1101,7 @@ export class BrainIntelligence {
|
|
|
1144
1101
|
patternMap.set(s.pattern, list);
|
|
1145
1102
|
}
|
|
1146
1103
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
const insert = db.prepare(
|
|
1150
|
-
`INSERT INTO brain_global_registry
|
|
1151
|
-
(pattern, domains, total_strength, avg_strength, domain_count, updated_at)
|
|
1152
|
-
VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
|
1153
|
-
);
|
|
1104
|
+
this.provider.run('DELETE FROM brain_global_registry');
|
|
1154
1105
|
|
|
1155
1106
|
let count = 0;
|
|
1156
1107
|
for (const [pattern, entries] of patternMap) {
|
|
@@ -1158,7 +1109,12 @@ export class BrainIntelligence {
|
|
|
1158
1109
|
const totalStrength = entries.reduce((sum, e) => sum + e.strength, 0);
|
|
1159
1110
|
const avgStrength = totalStrength / entries.length;
|
|
1160
1111
|
|
|
1161
|
-
|
|
1112
|
+
this.provider.run(
|
|
1113
|
+
`INSERT INTO brain_global_registry
|
|
1114
|
+
(pattern, domains, total_strength, avg_strength, domain_count, updated_at)
|
|
1115
|
+
VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
|
1116
|
+
[pattern, JSON.stringify(domains), totalStrength, avgStrength, domains.length],
|
|
1117
|
+
);
|
|
1162
1118
|
count++;
|
|
1163
1119
|
}
|
|
1164
1120
|
|
|
@@ -1166,8 +1122,6 @@ export class BrainIntelligence {
|
|
|
1166
1122
|
}
|
|
1167
1123
|
|
|
1168
1124
|
private buildDomainProfiles(strengths: PatternStrength[]): number {
|
|
1169
|
-
const db = this.vault.getDb();
|
|
1170
|
-
|
|
1171
1125
|
// Group strengths by domain
|
|
1172
1126
|
const domainMap = new Map<string, PatternStrength[]>();
|
|
1173
1127
|
for (const s of strengths) {
|
|
@@ -1176,13 +1130,7 @@ export class BrainIntelligence {
|
|
|
1176
1130
|
domainMap.set(s.domain, list);
|
|
1177
1131
|
}
|
|
1178
1132
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
const insert = db.prepare(
|
|
1182
|
-
`INSERT INTO brain_domain_profiles
|
|
1183
|
-
(domain, top_patterns, session_count, avg_session_duration, last_activity, updated_at)
|
|
1184
|
-
VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
|
1185
|
-
);
|
|
1133
|
+
this.provider.run('DELETE FROM brain_domain_profiles');
|
|
1186
1134
|
|
|
1187
1135
|
let count = 0;
|
|
1188
1136
|
for (const [domain, entries] of domainMap) {
|
|
@@ -1193,34 +1141,37 @@ export class BrainIntelligence {
|
|
|
1193
1141
|
}));
|
|
1194
1142
|
|
|
1195
1143
|
// Count sessions for this domain
|
|
1196
|
-
const sessionCount = (
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
).c;
|
|
1144
|
+
const sessionCount = this.provider.get<{ c: number }>(
|
|
1145
|
+
'SELECT COUNT(*) as c FROM brain_sessions WHERE domain = ?',
|
|
1146
|
+
[domain],
|
|
1147
|
+
)!.c;
|
|
1201
1148
|
|
|
1202
1149
|
// Average session duration (in minutes)
|
|
1203
|
-
const durationRow =
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
.get(domain) as { avg_min: number | null };
|
|
1150
|
+
const durationRow = this.provider.get<{ avg_min: number | null }>(
|
|
1151
|
+
`SELECT AVG(
|
|
1152
|
+
(julianday(ended_at) - julianday(started_at)) * 1440
|
|
1153
|
+
) as avg_min
|
|
1154
|
+
FROM brain_sessions
|
|
1155
|
+
WHERE domain = ? AND ended_at IS NOT NULL`,
|
|
1156
|
+
[domain],
|
|
1157
|
+
)!;
|
|
1212
1158
|
|
|
1213
1159
|
const lastActivity = entries.reduce(
|
|
1214
1160
|
(latest, e) => (e.lastUsed > latest ? e.lastUsed : latest),
|
|
1215
1161
|
'',
|
|
1216
1162
|
);
|
|
1217
1163
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1164
|
+
this.provider.run(
|
|
1165
|
+
`INSERT INTO brain_domain_profiles
|
|
1166
|
+
(domain, top_patterns, session_count, avg_session_duration, last_activity, updated_at)
|
|
1167
|
+
VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
|
1168
|
+
[
|
|
1169
|
+
domain,
|
|
1170
|
+
JSON.stringify(topPatterns),
|
|
1171
|
+
sessionCount,
|
|
1172
|
+
durationRow.avg_min ?? 0,
|
|
1173
|
+
lastActivity || new Date().toISOString(),
|
|
1174
|
+
],
|
|
1224
1175
|
);
|
|
1225
1176
|
count++;
|
|
1226
1177
|
}
|