@timmeck/brain-core 2.2.0 → 2.4.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/api/middleware.js +1 -0
- package/dist/api/middleware.js.map +1 -1
- package/dist/backup/service.d.ts +48 -0
- package/dist/backup/service.js +153 -0
- package/dist/backup/service.js.map +1 -0
- package/dist/causal/engine.d.ts +98 -0
- package/dist/causal/engine.js +265 -0
- package/dist/causal/engine.js.map +1 -0
- package/dist/export/service.d.ts +42 -0
- package/dist/export/service.js +118 -0
- package/dist/export/service.js.map +1 -0
- package/dist/hypothesis/engine.d.ts +104 -0
- package/dist/hypothesis/engine.js +386 -0
- package/dist/hypothesis/engine.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/meta-learning/engine.d.ts +108 -0
- package/dist/meta-learning/engine.js +275 -0
- package/dist/meta-learning/engine.js.map +1 -0
- package/dist/webhooks/service.d.ts +65 -0
- package/dist/webhooks/service.js +157 -0
- package/dist/webhooks/service.js.map +1 -0
- package/package.json +8 -2
- package/tsconfig.tsbuildinfo +1 -0
package/dist/api/middleware.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/api/middleware.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/api/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export interface BackupConfig {
|
|
3
|
+
backupDir?: string;
|
|
4
|
+
maxBackups?: number;
|
|
5
|
+
compress?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface BackupRecord {
|
|
8
|
+
filename: string;
|
|
9
|
+
path: string;
|
|
10
|
+
size: number;
|
|
11
|
+
created: string;
|
|
12
|
+
}
|
|
13
|
+
export interface RestoreResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
filename: string;
|
|
16
|
+
integrityOk: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare class BackupService {
|
|
19
|
+
private db;
|
|
20
|
+
private dbPath;
|
|
21
|
+
private logger;
|
|
22
|
+
private backupDir;
|
|
23
|
+
private maxBackups;
|
|
24
|
+
constructor(db: Database.Database, dbPath: string, config?: BackupConfig);
|
|
25
|
+
/** Create a backup of the current database. */
|
|
26
|
+
create(label?: string): BackupRecord;
|
|
27
|
+
/** List all available backups (newest first). */
|
|
28
|
+
list(): BackupRecord[];
|
|
29
|
+
/**
|
|
30
|
+
* Restore a database from a backup.
|
|
31
|
+
* CAUTION: This replaces the current database. The caller must close and reopen the DB connection.
|
|
32
|
+
*/
|
|
33
|
+
restore(filename: string): RestoreResult;
|
|
34
|
+
/** Check integrity of a database file. */
|
|
35
|
+
checkIntegrity(dbFilePath?: string): boolean;
|
|
36
|
+
/** Get current database size info. */
|
|
37
|
+
getInfo(): {
|
|
38
|
+
dbSize: number;
|
|
39
|
+
walSize: number;
|
|
40
|
+
backupCount: number;
|
|
41
|
+
backupTotalSize: number;
|
|
42
|
+
};
|
|
43
|
+
/** Delete a specific backup. */
|
|
44
|
+
delete(filename: string): boolean;
|
|
45
|
+
/** Auto-cleanup oldest backups beyond maxBackups. */
|
|
46
|
+
private autoCleanup;
|
|
47
|
+
private formatSize;
|
|
48
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { getLogger } from '../utils/logger.js';
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
// ── Service ─────────────────────────────────────────────
|
|
7
|
+
export class BackupService {
|
|
8
|
+
db;
|
|
9
|
+
dbPath;
|
|
10
|
+
logger = getLogger();
|
|
11
|
+
backupDir;
|
|
12
|
+
maxBackups;
|
|
13
|
+
constructor(db, dbPath, config) {
|
|
14
|
+
this.db = db;
|
|
15
|
+
this.dbPath = dbPath;
|
|
16
|
+
this.backupDir = config?.backupDir ?? path.join(path.dirname(dbPath), 'backups');
|
|
17
|
+
this.maxBackups = config?.maxBackups ?? 10;
|
|
18
|
+
fs.mkdirSync(this.backupDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
/** Create a backup of the current database. */
|
|
21
|
+
create(label) {
|
|
22
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
23
|
+
const suffix = label ? `-${label.replace(/[^a-zA-Z0-9-_]/g, '')}` : '';
|
|
24
|
+
const filename = `backup-${timestamp}${suffix}.db`;
|
|
25
|
+
const backupPath = path.join(this.backupDir, filename);
|
|
26
|
+
// Checkpoint WAL before copying for a consistent snapshot
|
|
27
|
+
this.db.pragma('wal_checkpoint(TRUNCATE)');
|
|
28
|
+
// Copy DB file synchronously
|
|
29
|
+
fs.copyFileSync(this.dbPath, backupPath);
|
|
30
|
+
// Verify integrity
|
|
31
|
+
const integrityOk = this.checkIntegrity(backupPath);
|
|
32
|
+
if (!integrityOk) {
|
|
33
|
+
this.logger.warn(`Backup integrity check failed: ${filename}`);
|
|
34
|
+
fs.unlinkSync(backupPath);
|
|
35
|
+
throw new Error('Backup integrity check failed');
|
|
36
|
+
}
|
|
37
|
+
this.logger.info(`Backup created: ${filename} (${this.formatSize(fs.statSync(backupPath).size)})`);
|
|
38
|
+
// Auto-cleanup old backups
|
|
39
|
+
this.autoCleanup();
|
|
40
|
+
const stat = fs.statSync(backupPath);
|
|
41
|
+
return {
|
|
42
|
+
filename,
|
|
43
|
+
path: backupPath,
|
|
44
|
+
size: stat.size,
|
|
45
|
+
created: stat.mtime.toISOString(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** List all available backups (newest first). */
|
|
49
|
+
list() {
|
|
50
|
+
if (!fs.existsSync(this.backupDir))
|
|
51
|
+
return [];
|
|
52
|
+
const files = fs.readdirSync(this.backupDir)
|
|
53
|
+
.filter(f => f.startsWith('backup-') && f.endsWith('.db'))
|
|
54
|
+
.sort()
|
|
55
|
+
.reverse();
|
|
56
|
+
return files.map(filename => {
|
|
57
|
+
const filePath = path.join(this.backupDir, filename);
|
|
58
|
+
const stat = fs.statSync(filePath);
|
|
59
|
+
return {
|
|
60
|
+
filename,
|
|
61
|
+
path: filePath,
|
|
62
|
+
size: stat.size,
|
|
63
|
+
created: stat.mtime.toISOString(),
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Restore a database from a backup.
|
|
69
|
+
* CAUTION: This replaces the current database. The caller must close and reopen the DB connection.
|
|
70
|
+
*/
|
|
71
|
+
restore(filename) {
|
|
72
|
+
const backupPath = path.join(this.backupDir, filename);
|
|
73
|
+
if (!fs.existsSync(backupPath)) {
|
|
74
|
+
throw new Error(`Backup not found: ${filename}`);
|
|
75
|
+
}
|
|
76
|
+
// Verify backup integrity before restore
|
|
77
|
+
const integrityOk = this.checkIntegrity(backupPath);
|
|
78
|
+
if (!integrityOk) {
|
|
79
|
+
throw new Error(`Backup integrity check failed: ${filename}`);
|
|
80
|
+
}
|
|
81
|
+
// Create a safety backup of current DB before overwriting
|
|
82
|
+
const safetyName = `pre-restore-${new Date().toISOString().replace(/[:.]/g, '-')}.db`;
|
|
83
|
+
const safetyPath = path.join(this.backupDir, safetyName);
|
|
84
|
+
this.db.pragma('wal_checkpoint(TRUNCATE)');
|
|
85
|
+
fs.copyFileSync(this.dbPath, safetyPath);
|
|
86
|
+
this.logger.info(`Safety backup created: ${safetyName}`);
|
|
87
|
+
// Close current DB, copy backup over, caller must reopen
|
|
88
|
+
this.db.close();
|
|
89
|
+
fs.copyFileSync(backupPath, this.dbPath);
|
|
90
|
+
// Clean up WAL/SHM files if they exist
|
|
91
|
+
try {
|
|
92
|
+
fs.unlinkSync(this.dbPath + '-wal');
|
|
93
|
+
}
|
|
94
|
+
catch { /* may not exist */ }
|
|
95
|
+
try {
|
|
96
|
+
fs.unlinkSync(this.dbPath + '-shm');
|
|
97
|
+
}
|
|
98
|
+
catch { /* may not exist */ }
|
|
99
|
+
this.logger.info(`Database restored from: ${filename}`);
|
|
100
|
+
return { success: true, filename, integrityOk };
|
|
101
|
+
}
|
|
102
|
+
/** Check integrity of a database file. */
|
|
103
|
+
checkIntegrity(dbFilePath) {
|
|
104
|
+
const filePath = dbFilePath ?? this.dbPath;
|
|
105
|
+
try {
|
|
106
|
+
const BetterSqlite3 = require('better-sqlite3');
|
|
107
|
+
const testDb = new BetterSqlite3(filePath, { readonly: true });
|
|
108
|
+
const result = testDb.pragma('integrity_check');
|
|
109
|
+
testDb.close();
|
|
110
|
+
return result.length === 1 && result[0].integrity_check === 'ok';
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Get current database size info. */
|
|
117
|
+
getInfo() {
|
|
118
|
+
const dbSize = fs.existsSync(this.dbPath) ? fs.statSync(this.dbPath).size : 0;
|
|
119
|
+
const walPath = this.dbPath + '-wal';
|
|
120
|
+
const walSize = fs.existsSync(walPath) ? fs.statSync(walPath).size : 0;
|
|
121
|
+
const backups = this.list();
|
|
122
|
+
const backupTotalSize = backups.reduce((sum, b) => sum + b.size, 0);
|
|
123
|
+
return { dbSize, walSize, backupCount: backups.length, backupTotalSize };
|
|
124
|
+
}
|
|
125
|
+
/** Delete a specific backup. */
|
|
126
|
+
delete(filename) {
|
|
127
|
+
const filePath = path.join(this.backupDir, filename);
|
|
128
|
+
if (!fs.existsSync(filePath))
|
|
129
|
+
return false;
|
|
130
|
+
fs.unlinkSync(filePath);
|
|
131
|
+
this.logger.info(`Backup deleted: ${filename}`);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
/** Auto-cleanup oldest backups beyond maxBackups. */
|
|
135
|
+
autoCleanup() {
|
|
136
|
+
const backups = this.list(); // sorted newest first
|
|
137
|
+
if (backups.length <= this.maxBackups)
|
|
138
|
+
return;
|
|
139
|
+
const toDelete = backups.slice(this.maxBackups);
|
|
140
|
+
for (const backup of toDelete) {
|
|
141
|
+
this.delete(backup.filename);
|
|
142
|
+
}
|
|
143
|
+
this.logger.info(`Auto-cleaned ${toDelete.length} old backup(s)`);
|
|
144
|
+
}
|
|
145
|
+
formatSize(bytes) {
|
|
146
|
+
if (bytes < 1024)
|
|
147
|
+
return `${bytes}B`;
|
|
148
|
+
if (bytes < 1024 * 1024)
|
|
149
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
150
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/backup/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export interface CausalEvent {
|
|
3
|
+
source: string;
|
|
4
|
+
type: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
data?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface CausalEdge {
|
|
9
|
+
id?: number;
|
|
10
|
+
cause: string;
|
|
11
|
+
effect: string;
|
|
12
|
+
strength: number;
|
|
13
|
+
confidence: number;
|
|
14
|
+
lag_ms: number;
|
|
15
|
+
sample_size: number;
|
|
16
|
+
direction: number;
|
|
17
|
+
}
|
|
18
|
+
export interface CausalPath {
|
|
19
|
+
chain: string[];
|
|
20
|
+
totalStrength: number;
|
|
21
|
+
totalLag: number;
|
|
22
|
+
}
|
|
23
|
+
export interface CausalAnalysis {
|
|
24
|
+
edges: CausalEdge[];
|
|
25
|
+
roots: string[];
|
|
26
|
+
leaves: string[];
|
|
27
|
+
strongestChain: CausalPath | null;
|
|
28
|
+
}
|
|
29
|
+
export declare function runCausalMigration(db: Database.Database): void;
|
|
30
|
+
/**
|
|
31
|
+
* Causal Inference Engine: detects cause→effect relationships between events.
|
|
32
|
+
*
|
|
33
|
+
* Research approach: Simplified Granger Causality.
|
|
34
|
+
*
|
|
35
|
+
* Granger causality asks: "Does knowing that event A happened improve
|
|
36
|
+
* our ability to predict event B?" If yes, A Granger-causes B.
|
|
37
|
+
*
|
|
38
|
+
* Algorithm:
|
|
39
|
+
* 1. Record all events with timestamps
|
|
40
|
+
* 2. For each pair of event types (A, B):
|
|
41
|
+
* a. Count how often B occurs within a time window after A
|
|
42
|
+
* b. Compare this to the baseline rate of B
|
|
43
|
+
* c. If B is significantly more likely after A → A causes B
|
|
44
|
+
* 3. Build a directed graph of causal relationships
|
|
45
|
+
* 4. Detect causal chains (A → B → C)
|
|
46
|
+
*/
|
|
47
|
+
export declare class CausalGraph {
|
|
48
|
+
private db;
|
|
49
|
+
private logger;
|
|
50
|
+
private maxWindowMs;
|
|
51
|
+
private minSamples;
|
|
52
|
+
private significanceThreshold;
|
|
53
|
+
constructor(db: Database.Database, config?: {
|
|
54
|
+
maxWindowMs?: number;
|
|
55
|
+
minSamples?: number;
|
|
56
|
+
significanceThreshold?: number;
|
|
57
|
+
});
|
|
58
|
+
/** Record an event for causal analysis. */
|
|
59
|
+
recordEvent(source: string, type: string, data?: unknown): void;
|
|
60
|
+
/**
|
|
61
|
+
* Run Granger causality analysis on all event pairs.
|
|
62
|
+
* This is the core research algorithm.
|
|
63
|
+
*/
|
|
64
|
+
analyze(): CausalEdge[];
|
|
65
|
+
/**
|
|
66
|
+
* Test if event type A Granger-causes event type B.
|
|
67
|
+
*
|
|
68
|
+
* Algorithm:
|
|
69
|
+
* 1. Get all timestamps for A and B
|
|
70
|
+
* 2. For each occurrence of A, check if B occurs within the window after
|
|
71
|
+
* 3. Calculate: P(B after A) vs P(B in any random window)
|
|
72
|
+
* 4. If ratio > threshold → A Granger-causes B
|
|
73
|
+
*/
|
|
74
|
+
private testGrangerCausality;
|
|
75
|
+
/** Get all detected causal edges (strongest first). */
|
|
76
|
+
getEdges(minStrength?: number): CausalEdge[];
|
|
77
|
+
/** Get causes of a specific event type. */
|
|
78
|
+
getCauses(eventType: string): CausalEdge[];
|
|
79
|
+
/** Get effects of a specific event type. */
|
|
80
|
+
getEffects(eventType: string): CausalEdge[];
|
|
81
|
+
/** Find causal chains (A → B → C). */
|
|
82
|
+
findChains(maxDepth?: number): CausalPath[];
|
|
83
|
+
/** Full causal analysis including graph structure. */
|
|
84
|
+
getAnalysis(): CausalAnalysis;
|
|
85
|
+
/** Get event type statistics. */
|
|
86
|
+
getEventStats(): Array<{
|
|
87
|
+
type: string;
|
|
88
|
+
count: number;
|
|
89
|
+
first_seen: number;
|
|
90
|
+
last_seen: number;
|
|
91
|
+
}>;
|
|
92
|
+
private getEventTypes;
|
|
93
|
+
private getTimeRange;
|
|
94
|
+
private upsertEdge;
|
|
95
|
+
private findRoots;
|
|
96
|
+
private findLeaves;
|
|
97
|
+
private dfs;
|
|
98
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
// ── Migration ───────────────────────────────────────────
|
|
3
|
+
export function runCausalMigration(db) {
|
|
4
|
+
db.exec(`
|
|
5
|
+
CREATE TABLE IF NOT EXISTS causal_events (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
source TEXT NOT NULL,
|
|
8
|
+
type TEXT NOT NULL,
|
|
9
|
+
timestamp INTEGER NOT NULL,
|
|
10
|
+
data TEXT,
|
|
11
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS causal_edges (
|
|
15
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
|
+
cause TEXT NOT NULL,
|
|
17
|
+
effect TEXT NOT NULL,
|
|
18
|
+
strength REAL NOT NULL DEFAULT 0,
|
|
19
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
20
|
+
lag_ms REAL NOT NULL DEFAULT 0,
|
|
21
|
+
sample_size INTEGER NOT NULL DEFAULT 0,
|
|
22
|
+
direction REAL NOT NULL DEFAULT 1,
|
|
23
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
24
|
+
UNIQUE(cause, effect)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_causal_events_type ON causal_events(type);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_causal_events_timestamp ON causal_events(timestamp);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_causal_edges_strength ON causal_edges(strength DESC);
|
|
30
|
+
`);
|
|
31
|
+
}
|
|
32
|
+
// ── Engine ───────────────────────────────────────────────
|
|
33
|
+
/**
|
|
34
|
+
* Causal Inference Engine: detects cause→effect relationships between events.
|
|
35
|
+
*
|
|
36
|
+
* Research approach: Simplified Granger Causality.
|
|
37
|
+
*
|
|
38
|
+
* Granger causality asks: "Does knowing that event A happened improve
|
|
39
|
+
* our ability to predict event B?" If yes, A Granger-causes B.
|
|
40
|
+
*
|
|
41
|
+
* Algorithm:
|
|
42
|
+
* 1. Record all events with timestamps
|
|
43
|
+
* 2. For each pair of event types (A, B):
|
|
44
|
+
* a. Count how often B occurs within a time window after A
|
|
45
|
+
* b. Compare this to the baseline rate of B
|
|
46
|
+
* c. If B is significantly more likely after A → A causes B
|
|
47
|
+
* 3. Build a directed graph of causal relationships
|
|
48
|
+
* 4. Detect causal chains (A → B → C)
|
|
49
|
+
*/
|
|
50
|
+
export class CausalGraph {
|
|
51
|
+
db;
|
|
52
|
+
logger = getLogger();
|
|
53
|
+
maxWindowMs; // max time lag to consider
|
|
54
|
+
minSamples; // minimum observations for significance
|
|
55
|
+
significanceThreshold; // minimum ratio to baseline for significance
|
|
56
|
+
constructor(db, config) {
|
|
57
|
+
this.db = db;
|
|
58
|
+
runCausalMigration(db);
|
|
59
|
+
this.maxWindowMs = config?.maxWindowMs ?? 300_000; // 5 minutes
|
|
60
|
+
this.minSamples = config?.minSamples ?? 5;
|
|
61
|
+
this.significanceThreshold = config?.significanceThreshold ?? 1.5; // 50% above baseline
|
|
62
|
+
}
|
|
63
|
+
/** Record an event for causal analysis. */
|
|
64
|
+
recordEvent(source, type, data) {
|
|
65
|
+
this.db.prepare(`
|
|
66
|
+
INSERT INTO causal_events (source, type, timestamp, data)
|
|
67
|
+
VALUES (?, ?, ?, ?)
|
|
68
|
+
`).run(source, type, Date.now(), data ? JSON.stringify(data) : null);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Run Granger causality analysis on all event pairs.
|
|
72
|
+
* This is the core research algorithm.
|
|
73
|
+
*/
|
|
74
|
+
analyze() {
|
|
75
|
+
const eventTypes = this.getEventTypes();
|
|
76
|
+
if (eventTypes.length < 2)
|
|
77
|
+
return [];
|
|
78
|
+
const edges = [];
|
|
79
|
+
// For each pair of event types, test for Granger causality
|
|
80
|
+
for (const cause of eventTypes) {
|
|
81
|
+
for (const effect of eventTypes) {
|
|
82
|
+
if (cause === effect)
|
|
83
|
+
continue;
|
|
84
|
+
const edge = this.testGrangerCausality(cause, effect);
|
|
85
|
+
if (edge) {
|
|
86
|
+
this.upsertEdge(edge);
|
|
87
|
+
edges.push(edge);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
this.logger.info(`Causal analysis complete: ${edges.length} causal relationships detected`);
|
|
92
|
+
return edges;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Test if event type A Granger-causes event type B.
|
|
96
|
+
*
|
|
97
|
+
* Algorithm:
|
|
98
|
+
* 1. Get all timestamps for A and B
|
|
99
|
+
* 2. For each occurrence of A, check if B occurs within the window after
|
|
100
|
+
* 3. Calculate: P(B after A) vs P(B in any random window)
|
|
101
|
+
* 4. If ratio > threshold → A Granger-causes B
|
|
102
|
+
*/
|
|
103
|
+
testGrangerCausality(causeType, effectType) {
|
|
104
|
+
const causeEvents = this.db.prepare('SELECT timestamp FROM causal_events WHERE type = ? ORDER BY timestamp').all(causeType);
|
|
105
|
+
const effectEvents = this.db.prepare('SELECT timestamp FROM causal_events WHERE type = ? ORDER BY timestamp').all(effectType);
|
|
106
|
+
if (causeEvents.length < this.minSamples || effectEvents.length < this.minSamples) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
// Calculate: how often does effectType occur within maxWindowMs after causeType?
|
|
110
|
+
let followCount = 0;
|
|
111
|
+
let totalLag = 0;
|
|
112
|
+
for (const cause of causeEvents) {
|
|
113
|
+
// Find the first effect event within the window
|
|
114
|
+
for (const effect of effectEvents) {
|
|
115
|
+
const lag = effect.timestamp - cause.timestamp;
|
|
116
|
+
if (lag > 0 && lag <= this.maxWindowMs) {
|
|
117
|
+
followCount++;
|
|
118
|
+
totalLag += lag;
|
|
119
|
+
break; // only count first occurrence per cause event
|
|
120
|
+
}
|
|
121
|
+
if (lag > this.maxWindowMs)
|
|
122
|
+
break; // past window, stop looking
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (followCount < this.minSamples)
|
|
126
|
+
return null;
|
|
127
|
+
// P(effect follows cause) = followCount / causeEvents.length
|
|
128
|
+
const pFollows = followCount / causeEvents.length;
|
|
129
|
+
// Baseline: P(effect in any random window of same size)
|
|
130
|
+
// Estimate: total effect events / total time * window size
|
|
131
|
+
const timeRange = this.getTimeRange();
|
|
132
|
+
if (timeRange <= 0)
|
|
133
|
+
return null;
|
|
134
|
+
const baselineRate = (effectEvents.length / timeRange) * this.maxWindowMs;
|
|
135
|
+
const pBaseline = Math.min(1, baselineRate);
|
|
136
|
+
// Significance: is the conditional probability significantly higher than baseline?
|
|
137
|
+
if (pBaseline <= 0)
|
|
138
|
+
return null;
|
|
139
|
+
const ratio = pFollows / pBaseline;
|
|
140
|
+
if (ratio < this.significanceThreshold)
|
|
141
|
+
return null;
|
|
142
|
+
// Calculate strength (0-1 normalized)
|
|
143
|
+
const strength = Math.min(1, (ratio - 1) / (this.significanceThreshold * 2));
|
|
144
|
+
// Confidence based on sample size (logistic curve, plateaus around 20 samples)
|
|
145
|
+
const confidence = 1 - 1 / (1 + followCount / 10);
|
|
146
|
+
// Average lag
|
|
147
|
+
const avgLag = followCount > 0 ? totalLag / followCount : 0;
|
|
148
|
+
return {
|
|
149
|
+
cause: causeType,
|
|
150
|
+
effect: effectType,
|
|
151
|
+
strength,
|
|
152
|
+
confidence,
|
|
153
|
+
lag_ms: avgLag,
|
|
154
|
+
sample_size: followCount,
|
|
155
|
+
direction: 1, // cause increases likelihood of effect
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/** Get all detected causal edges (strongest first). */
|
|
159
|
+
getEdges(minStrength = 0) {
|
|
160
|
+
return this.db.prepare('SELECT * FROM causal_edges WHERE strength >= ? ORDER BY strength DESC').all(minStrength);
|
|
161
|
+
}
|
|
162
|
+
/** Get causes of a specific event type. */
|
|
163
|
+
getCauses(eventType) {
|
|
164
|
+
return this.db.prepare('SELECT * FROM causal_edges WHERE effect = ? ORDER BY strength DESC').all(eventType);
|
|
165
|
+
}
|
|
166
|
+
/** Get effects of a specific event type. */
|
|
167
|
+
getEffects(eventType) {
|
|
168
|
+
return this.db.prepare('SELECT * FROM causal_edges WHERE cause = ? ORDER BY strength DESC').all(eventType);
|
|
169
|
+
}
|
|
170
|
+
/** Find causal chains (A → B → C). */
|
|
171
|
+
findChains(maxDepth = 4) {
|
|
172
|
+
const edges = this.getEdges(0.1);
|
|
173
|
+
const adjacency = new Map();
|
|
174
|
+
for (const edge of edges) {
|
|
175
|
+
if (!adjacency.has(edge.cause))
|
|
176
|
+
adjacency.set(edge.cause, []);
|
|
177
|
+
adjacency.get(edge.cause).push(edge);
|
|
178
|
+
}
|
|
179
|
+
const chains = [];
|
|
180
|
+
const roots = this.findRoots(edges);
|
|
181
|
+
for (const root of roots) {
|
|
182
|
+
this.dfs(root, [root], 0, 0, adjacency, maxDepth, new Set(), chains);
|
|
183
|
+
}
|
|
184
|
+
// Sort by total strength descending
|
|
185
|
+
chains.sort((a, b) => b.totalStrength - a.totalStrength);
|
|
186
|
+
return chains.slice(0, 20); // top 20 chains
|
|
187
|
+
}
|
|
188
|
+
/** Full causal analysis including graph structure. */
|
|
189
|
+
getAnalysis() {
|
|
190
|
+
const edges = this.getEdges();
|
|
191
|
+
const roots = this.findRoots(edges);
|
|
192
|
+
const leaves = this.findLeaves(edges);
|
|
193
|
+
const chains = this.findChains();
|
|
194
|
+
return {
|
|
195
|
+
edges,
|
|
196
|
+
roots,
|
|
197
|
+
leaves,
|
|
198
|
+
strongestChain: chains.length > 0 ? chains[0] : null,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/** Get event type statistics. */
|
|
202
|
+
getEventStats() {
|
|
203
|
+
return this.db.prepare(`
|
|
204
|
+
SELECT type, COUNT(*) as count,
|
|
205
|
+
MIN(timestamp) as first_seen,
|
|
206
|
+
MAX(timestamp) as last_seen
|
|
207
|
+
FROM causal_events
|
|
208
|
+
GROUP BY type
|
|
209
|
+
ORDER BY count DESC
|
|
210
|
+
`).all();
|
|
211
|
+
}
|
|
212
|
+
// ── Private helpers ─────────────────────────────────
|
|
213
|
+
getEventTypes() {
|
|
214
|
+
const rows = this.db.prepare('SELECT DISTINCT type FROM causal_events').all();
|
|
215
|
+
return rows.map(r => r.type);
|
|
216
|
+
}
|
|
217
|
+
getTimeRange() {
|
|
218
|
+
const row = this.db.prepare('SELECT MIN(timestamp) as min_ts, MAX(timestamp) as max_ts FROM causal_events').get();
|
|
219
|
+
if (!row.min_ts || !row.max_ts)
|
|
220
|
+
return 0;
|
|
221
|
+
return row.max_ts - row.min_ts;
|
|
222
|
+
}
|
|
223
|
+
upsertEdge(edge) {
|
|
224
|
+
this.db.prepare(`
|
|
225
|
+
INSERT INTO causal_edges (cause, effect, strength, confidence, lag_ms, sample_size, direction)
|
|
226
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
227
|
+
ON CONFLICT(cause, effect) DO UPDATE SET
|
|
228
|
+
strength = ?, confidence = ?, lag_ms = ?, sample_size = ?, direction = ?,
|
|
229
|
+
updated_at = datetime('now')
|
|
230
|
+
`).run(edge.cause, edge.effect, edge.strength, edge.confidence, edge.lag_ms, edge.sample_size, edge.direction, edge.strength, edge.confidence, edge.lag_ms, edge.sample_size, edge.direction);
|
|
231
|
+
}
|
|
232
|
+
findRoots(edges) {
|
|
233
|
+
const causes = new Set(edges.map(e => e.cause));
|
|
234
|
+
const effects = new Set(edges.map(e => e.effect));
|
|
235
|
+
return [...causes].filter(c => !effects.has(c));
|
|
236
|
+
}
|
|
237
|
+
findLeaves(edges) {
|
|
238
|
+
const causes = new Set(edges.map(e => e.cause));
|
|
239
|
+
const effects = new Set(edges.map(e => e.effect));
|
|
240
|
+
return [...effects].filter(e => !causes.has(e));
|
|
241
|
+
}
|
|
242
|
+
dfs(node, path, totalStrength, totalLag, adjacency, maxDepth, visited, results) {
|
|
243
|
+
if (path.length >= 3) {
|
|
244
|
+
// Record any path of length 3+ as a chain
|
|
245
|
+
results.push({
|
|
246
|
+
chain: [...path],
|
|
247
|
+
totalStrength: totalStrength / (path.length - 1), // average strength
|
|
248
|
+
totalLag,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (path.length >= maxDepth)
|
|
252
|
+
return;
|
|
253
|
+
const neighbors = adjacency.get(node) ?? [];
|
|
254
|
+
for (const edge of neighbors) {
|
|
255
|
+
if (visited.has(edge.effect))
|
|
256
|
+
continue;
|
|
257
|
+
visited.add(edge.effect);
|
|
258
|
+
path.push(edge.effect);
|
|
259
|
+
this.dfs(edge.effect, path, totalStrength + edge.strength, totalLag + edge.lag_ms, adjacency, maxDepth, visited, results);
|
|
260
|
+
path.pop();
|
|
261
|
+
visited.delete(edge.effect);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/causal/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export interface ExportOptions {
|
|
3
|
+
table: string;
|
|
4
|
+
format: 'json' | 'csv';
|
|
5
|
+
columns?: string[];
|
|
6
|
+
where?: string;
|
|
7
|
+
orderBy?: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
dateColumn?: string;
|
|
10
|
+
dateFrom?: string;
|
|
11
|
+
dateTo?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ExportResult {
|
|
14
|
+
table: string;
|
|
15
|
+
format: 'json' | 'csv';
|
|
16
|
+
rowCount: number;
|
|
17
|
+
data: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class ExportService {
|
|
20
|
+
private db;
|
|
21
|
+
private logger;
|
|
22
|
+
constructor(db: Database.Database);
|
|
23
|
+
/** Get list of all tables in the database. */
|
|
24
|
+
listTables(): string[];
|
|
25
|
+
/** Get column info for a table. */
|
|
26
|
+
getColumns(table: string): {
|
|
27
|
+
name: string;
|
|
28
|
+
type: string;
|
|
29
|
+
}[];
|
|
30
|
+
/** Export data from a table. */
|
|
31
|
+
export(options: ExportOptions): ExportResult;
|
|
32
|
+
/** Export multiple tables at once (JSON only). */
|
|
33
|
+
exportAll(tables?: string[], format?: 'json' | 'csv'): Record<string, ExportResult>;
|
|
34
|
+
/** Get row counts for all tables. */
|
|
35
|
+
getStats(): Record<string, number>;
|
|
36
|
+
/** Convert array of objects to CSV string. */
|
|
37
|
+
private toCsv;
|
|
38
|
+
/** Validate table name to prevent SQL injection. */
|
|
39
|
+
private validateTableName;
|
|
40
|
+
/** Validate column name. */
|
|
41
|
+
private validateColumnName;
|
|
42
|
+
}
|