@obsidicore/cascade-engine 0.2.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/LICENSE +21 -0
- package/dist/cascade/checkpoints.d.ts +55 -0
- package/dist/cascade/checkpoints.js +123 -0
- package/dist/cascade/checkpoints.js.map +1 -0
- package/dist/cascade/engine.d.ts +72 -0
- package/dist/cascade/engine.js +170 -0
- package/dist/cascade/engine.js.map +1 -0
- package/dist/cascade/gates.d.ts +46 -0
- package/dist/cascade/gates.js +199 -0
- package/dist/cascade/gates.js.map +1 -0
- package/dist/cascade/research.d.ts +50 -0
- package/dist/cascade/research.js +127 -0
- package/dist/cascade/research.js.map +1 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +165 -0
- package/dist/cli.js.map +1 -0
- package/dist/control/kalman.d.ts +53 -0
- package/dist/control/kalman.js +83 -0
- package/dist/control/kalman.js.map +1 -0
- package/dist/control/pid.d.ts +57 -0
- package/dist/control/pid.js +95 -0
- package/dist/control/pid.js.map +1 -0
- package/dist/control/stability.d.ts +42 -0
- package/dist/control/stability.js +117 -0
- package/dist/control/stability.js.map +1 -0
- package/dist/db/index.d.ts +26 -0
- package/dist/db/index.js +116 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.sql +282 -0
- package/dist/graph/amem.d.ts +80 -0
- package/dist/graph/amem.js +190 -0
- package/dist/graph/amem.js.map +1 -0
- package/dist/graph/entities.d.ts +66 -0
- package/dist/graph/entities.js +187 -0
- package/dist/graph/entities.js.map +1 -0
- package/dist/graph/queries.d.ts +48 -0
- package/dist/graph/queries.js +176 -0
- package/dist/graph/queries.js.map +1 -0
- package/dist/hitl/dashboard.d.ts +51 -0
- package/dist/hitl/dashboard.js +135 -0
- package/dist/hitl/dashboard.js.map +1 -0
- package/dist/hitl/interventions.d.ts +36 -0
- package/dist/hitl/interventions.js +150 -0
- package/dist/hitl/interventions.js.map +1 -0
- package/dist/hitl/steering.d.ts +37 -0
- package/dist/hitl/steering.js +118 -0
- package/dist/hitl/steering.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +701 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/consolidation.d.ts +51 -0
- package/dist/memory/consolidation.js +122 -0
- package/dist/memory/consolidation.js.map +1 -0
- package/dist/memory/ncd.d.ts +40 -0
- package/dist/memory/ncd.js +90 -0
- package/dist/memory/ncd.js.map +1 -0
- package/dist/memory/sm2.d.ts +44 -0
- package/dist/memory/sm2.js +119 -0
- package/dist/memory/sm2.js.map +1 -0
- package/dist/memory/tiers.d.ts +49 -0
- package/dist/memory/tiers.js +145 -0
- package/dist/memory/tiers.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +6 -0
- package/dist/server.js.map +1 -0
- package/dist/trust/ingestion.d.ts +38 -0
- package/dist/trust/ingestion.js +147 -0
- package/dist/trust/ingestion.js.map +1 -0
- package/dist/trust/patterns.d.ts +26 -0
- package/dist/trust/patterns.js +78 -0
- package/dist/trust/patterns.js.map +1 -0
- package/dist/trust/scoring.d.ts +39 -0
- package/dist/trust/scoring.js +206 -0
- package/dist/trust/scoring.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kalman Filter — Confidence fusion from multiple noisy sources
|
|
3
|
+
*
|
|
4
|
+
* K = P / (P + R)
|
|
5
|
+
* confidence += K × (measurement - confidence)
|
|
6
|
+
* uncertainty = (1 - K) × P
|
|
7
|
+
*
|
|
8
|
+
* High R (unreliable source) → low K → measurement discounted
|
|
9
|
+
* High P (uncertain claim) → high K → updated aggressively
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Create initial Kalman state for a new finding.
|
|
13
|
+
*/
|
|
14
|
+
export function createKalmanState(initialEstimate = 0.5, initialUncertainty = 1.0) {
|
|
15
|
+
return {
|
|
16
|
+
estimate: initialEstimate,
|
|
17
|
+
uncertainty: initialUncertainty,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Update Kalman estimate with a new measurement.
|
|
22
|
+
*
|
|
23
|
+
* @param state Current Kalman state
|
|
24
|
+
* @param measurement New confidence measurement (0-1)
|
|
25
|
+
* @param measurementNoise R — noise of the measurement source (higher = less reliable)
|
|
26
|
+
* @returns Updated state
|
|
27
|
+
*/
|
|
28
|
+
export function kalmanUpdate(state, measurement, measurementNoise) {
|
|
29
|
+
// Kalman gain
|
|
30
|
+
const K = state.uncertainty / (state.uncertainty + measurementNoise);
|
|
31
|
+
// Update estimate
|
|
32
|
+
const newEstimate = state.estimate + K * (measurement - state.estimate);
|
|
33
|
+
// Update uncertainty
|
|
34
|
+
const newUncertainty = (1 - K) * state.uncertainty;
|
|
35
|
+
return {
|
|
36
|
+
estimate: Math.max(0, Math.min(1, newEstimate)),
|
|
37
|
+
uncertainty: newUncertainty,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fuse multiple measurements at once.
|
|
42
|
+
* Each measurement has its own noise level based on source reliability.
|
|
43
|
+
*/
|
|
44
|
+
export function fuseConfidence(measurements, initialEstimate = 0.5, initialUncertainty = 1.0) {
|
|
45
|
+
let state = createKalmanState(initialEstimate, initialUncertainty);
|
|
46
|
+
for (const m of measurements) {
|
|
47
|
+
state = kalmanUpdate(state, m.value, m.noise);
|
|
48
|
+
}
|
|
49
|
+
return state;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Map source type to measurement noise.
|
|
53
|
+
* Lower noise = more trusted source.
|
|
54
|
+
*/
|
|
55
|
+
export function sourceToNoise(sourceType, trustScore) {
|
|
56
|
+
const baseNoise = {
|
|
57
|
+
primary: 0.1, // Peer-reviewed → very reliable
|
|
58
|
+
secondary: 0.3, // Blogs, tutorials
|
|
59
|
+
tertiary: 0.6, // Forums, social media
|
|
60
|
+
};
|
|
61
|
+
const base = baseNoise[sourceType || ''] ?? 0.5;
|
|
62
|
+
// Modulate by trust score — low trust = higher noise
|
|
63
|
+
return base * (2 - trustScore); // Range: base*1 to base*2
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Batch update: apply multiple findings' confidence to a single claim.
|
|
67
|
+
* Returns the fused confidence and remaining uncertainty.
|
|
68
|
+
*/
|
|
69
|
+
export function batchFuseForClaim(existingConfidence, existingUncertainty, newMeasurements) {
|
|
70
|
+
let state = {
|
|
71
|
+
estimate: existingConfidence,
|
|
72
|
+
uncertainty: existingUncertainty,
|
|
73
|
+
};
|
|
74
|
+
for (const m of newMeasurements) {
|
|
75
|
+
const noise = sourceToNoise(m.sourceType, m.trustScore);
|
|
76
|
+
state = kalmanUpdate(state, m.confidence, noise);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
confidence: state.estimate,
|
|
80
|
+
uncertainty: state.uncertainty,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=kalman.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kalman.js","sourceRoot":"","sources":["../../src/control/kalman.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,kBAA0B,GAAG,EAC7B,qBAA6B,GAAG;IAEhC,OAAO;QACL,QAAQ,EAAE,eAAe;QACzB,WAAW,EAAE,kBAAkB;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAkB,EAClB,WAAmB,EACnB,gBAAwB;IAExB,cAAc;IACd,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,GAAG,CAAC,KAAK,CAAC,WAAW,GAAG,gBAAgB,CAAC,CAAC;IAErE,kBAAkB;IAClB,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,GAAG,CAAC,GAAG,CAAC,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IAExE,qBAAqB;IACrB,MAAM,cAAc,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC;IAEnD,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;QAC/C,WAAW,EAAE,cAAc;KAC5B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,YAAgD,EAChD,kBAA0B,GAAG,EAC7B,qBAA6B,GAAG;IAEhC,IAAI,KAAK,GAAG,iBAAiB,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;IAEnE,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,UAA8B,EAAE,UAAkB;IAC9E,MAAM,SAAS,GAA2B;QACxC,OAAO,EAAE,GAAG,EAAM,gCAAgC;QAClD,SAAS,EAAE,GAAG,EAAI,mBAAmB;QACrC,QAAQ,EAAE,GAAG,EAAK,uBAAuB;KAC1C,CAAC;IAEF,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC;IAEhD,qDAAqD;IACrD,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,0BAA0B;AAC5D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,kBAA0B,EAC1B,mBAA2B,EAC3B,eAAkF;IAElF,IAAI,KAAK,GAAgB;QACvB,QAAQ,EAAE,kBAAkB;QAC5B,WAAW,EAAE,mBAAmB;KACjC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,eAAe,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,aAAa,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;QACxD,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;IAED,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,QAAQ;QAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PID Controller — Self-regulation for research cascade
|
|
3
|
+
*
|
|
4
|
+
* Maps control theory to research strategy:
|
|
5
|
+
* - Kp (Proportional): Immediate response to knowledge gaps
|
|
6
|
+
* - Ki (Integral): Breaks out of chronic stagnation loops
|
|
7
|
+
* - Kd (Derivative): Dampens sudden contradictory information
|
|
8
|
+
*
|
|
9
|
+
* Anti-windup cap on integral. Lyapunov stability detection.
|
|
10
|
+
*
|
|
11
|
+
* Error signal components:
|
|
12
|
+
* error = coverage_gap*0.3 + low_confidence*0.3 + contradictions*0.2 + depth_gap*0.2
|
|
13
|
+
*
|
|
14
|
+
* Output maps to: {searchBreadth, searchDepth, explorationRate}
|
|
15
|
+
*/
|
|
16
|
+
export interface PIDState {
|
|
17
|
+
kp: number;
|
|
18
|
+
ki: number;
|
|
19
|
+
kd: number;
|
|
20
|
+
error: number;
|
|
21
|
+
prevError: number;
|
|
22
|
+
integral: number;
|
|
23
|
+
derivative: number;
|
|
24
|
+
output: number;
|
|
25
|
+
integralMax: number;
|
|
26
|
+
history: number[];
|
|
27
|
+
}
|
|
28
|
+
export interface PIDOutput {
|
|
29
|
+
searchBreadth: number;
|
|
30
|
+
searchDepth: number;
|
|
31
|
+
explorationRate: number;
|
|
32
|
+
}
|
|
33
|
+
export interface ErrorComponents {
|
|
34
|
+
coverageGap: number;
|
|
35
|
+
lowConfidence: number;
|
|
36
|
+
contradictions: number;
|
|
37
|
+
depthGap: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create initial PID state with tuned defaults.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createPIDState(kp?: number, ki?: number, kd?: number): PIDState;
|
|
43
|
+
/**
|
|
44
|
+
* Compute composite error from research state.
|
|
45
|
+
*/
|
|
46
|
+
export declare function computeError(components: ErrorComponents): number;
|
|
47
|
+
/**
|
|
48
|
+
* Update PID controller with new error measurement.
|
|
49
|
+
* Returns control output and updated state.
|
|
50
|
+
*/
|
|
51
|
+
export declare function updatePID(state: PIDState, error: number): PIDOutput;
|
|
52
|
+
/**
|
|
53
|
+
* Auto-tune Kp when oscillation detected.
|
|
54
|
+
* Reduces Kp by 0.7× on alternating error signs.
|
|
55
|
+
*/
|
|
56
|
+
export declare function autoTuneKp(state: PIDState): boolean;
|
|
57
|
+
//# sourceMappingURL=pid.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PID Controller — Self-regulation for research cascade
|
|
3
|
+
*
|
|
4
|
+
* Maps control theory to research strategy:
|
|
5
|
+
* - Kp (Proportional): Immediate response to knowledge gaps
|
|
6
|
+
* - Ki (Integral): Breaks out of chronic stagnation loops
|
|
7
|
+
* - Kd (Derivative): Dampens sudden contradictory information
|
|
8
|
+
*
|
|
9
|
+
* Anti-windup cap on integral. Lyapunov stability detection.
|
|
10
|
+
*
|
|
11
|
+
* Error signal components:
|
|
12
|
+
* error = coverage_gap*0.3 + low_confidence*0.3 + contradictions*0.2 + depth_gap*0.2
|
|
13
|
+
*
|
|
14
|
+
* Output maps to: {searchBreadth, searchDepth, explorationRate}
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Create initial PID state with tuned defaults.
|
|
18
|
+
*/
|
|
19
|
+
export function createPIDState(kp = 1.2, ki = 0.05, kd = 0.1) {
|
|
20
|
+
return {
|
|
21
|
+
kp, ki, kd,
|
|
22
|
+
error: 0,
|
|
23
|
+
prevError: 0,
|
|
24
|
+
integral: 0,
|
|
25
|
+
derivative: 0,
|
|
26
|
+
output: 0.5, // Start neutral
|
|
27
|
+
integralMax: 2.0,
|
|
28
|
+
history: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compute composite error from research state.
|
|
33
|
+
*/
|
|
34
|
+
export function computeError(components) {
|
|
35
|
+
return (components.coverageGap * 0.3 +
|
|
36
|
+
components.lowConfidence * 0.3 +
|
|
37
|
+
components.contradictions * 0.2 +
|
|
38
|
+
components.depthGap * 0.2);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Update PID controller with new error measurement.
|
|
42
|
+
* Returns control output and updated state.
|
|
43
|
+
*/
|
|
44
|
+
export function updatePID(state, error) {
|
|
45
|
+
// Proportional
|
|
46
|
+
const P = state.kp * error;
|
|
47
|
+
// Integral with anti-windup
|
|
48
|
+
state.integral = clamp(state.integral + error, -state.integralMax, state.integralMax);
|
|
49
|
+
const I = state.ki * state.integral;
|
|
50
|
+
// Derivative (using previous call's error)
|
|
51
|
+
state.derivative = error - state.error;
|
|
52
|
+
const D = state.kd * state.derivative;
|
|
53
|
+
// Combined output, clamped 0-1
|
|
54
|
+
state.output = clamp(P + I + D, 0, 1);
|
|
55
|
+
state.prevError = state.error;
|
|
56
|
+
state.error = error;
|
|
57
|
+
state.history.push(error);
|
|
58
|
+
// Keep history bounded
|
|
59
|
+
if (state.history.length > 20)
|
|
60
|
+
state.history.shift();
|
|
61
|
+
// Map output to research parameters
|
|
62
|
+
return mapOutputToStrategy(state.output);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Map PID output (0-1) to concrete research strategy parameters.
|
|
66
|
+
*/
|
|
67
|
+
function mapOutputToStrategy(output) {
|
|
68
|
+
return {
|
|
69
|
+
searchBreadth: clamp(output * 0.8 + 0.1, 0, 1), // 0.1-0.9 range
|
|
70
|
+
searchDepth: clamp((1 - output) * 0.8 + 0.1, 0, 1), // Inverse of breadth
|
|
71
|
+
explorationRate: clamp(output, 0, 1),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Auto-tune Kp when oscillation detected.
|
|
76
|
+
* Reduces Kp by 0.7× on alternating error signs.
|
|
77
|
+
*/
|
|
78
|
+
export function autoTuneKp(state) {
|
|
79
|
+
if (state.history.length < 4)
|
|
80
|
+
return false;
|
|
81
|
+
const recent = state.history.slice(-4);
|
|
82
|
+
const signs = recent.map((v, i) => i > 0 ? Math.sign(v - recent[i - 1]) : 0).slice(1);
|
|
83
|
+
// Check for alternating signs (oscillation)
|
|
84
|
+
const isOscillating = signs.length >= 3 &&
|
|
85
|
+
signs[0] !== signs[1] && signs[1] !== signs[2];
|
|
86
|
+
if (isOscillating) {
|
|
87
|
+
state.kp *= 0.7;
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
function clamp(value, min, max) {
|
|
93
|
+
return Math.max(min, Math.min(max, value));
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=pid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pid.js","sourceRoot":"","sources":["../../src/control/pid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lyapunov Stability Detection
|
|
3
|
+
*
|
|
4
|
+
* Monitors cascade convergence by tracking a Lyapunov-like function:
|
|
5
|
+
* V(t) = weighted sum of error metrics
|
|
6
|
+
*
|
|
7
|
+
* ΔV < 0 → converging (good)
|
|
8
|
+
* ΔV alternating signs → oscillating (reduce Kp by 0.7×)
|
|
9
|
+
* ΔV all positive → diverging (emergency synthesis)
|
|
10
|
+
*/
|
|
11
|
+
export type StabilityState = 'converging' | 'oscillating' | 'diverging' | 'insufficient_data';
|
|
12
|
+
export interface StabilityAnalysis {
|
|
13
|
+
state: StabilityState;
|
|
14
|
+
lyapunovValues: number[];
|
|
15
|
+
deltas: number[];
|
|
16
|
+
trend: number;
|
|
17
|
+
recommendation: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Compute Lyapunov-like function from error components.
|
|
21
|
+
* V(t) = Σ wᵢ × eᵢ² (quadratic form — always non-negative)
|
|
22
|
+
*/
|
|
23
|
+
export declare function computeLyapunov(errors: {
|
|
24
|
+
coverageGap: number;
|
|
25
|
+
lowConfidence: number;
|
|
26
|
+
contradictions: number;
|
|
27
|
+
depthGap: number;
|
|
28
|
+
}): number;
|
|
29
|
+
/**
|
|
30
|
+
* Analyze stability from a sequence of Lyapunov values.
|
|
31
|
+
* Requires at least 3 data points.
|
|
32
|
+
*/
|
|
33
|
+
export declare function analyzeStability(lyapunovHistory: number[]): StabilityAnalysis;
|
|
34
|
+
/**
|
|
35
|
+
* Check if the cascade has reached a convergence criterion.
|
|
36
|
+
* Uses both Lyapunov stability AND absolute error threshold.
|
|
37
|
+
*/
|
|
38
|
+
export declare function hasConverged(lyapunovHistory: number[], absoluteThreshold?: number): {
|
|
39
|
+
converged: boolean;
|
|
40
|
+
reason: string;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=stability.d.ts.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lyapunov Stability Detection
|
|
3
|
+
*
|
|
4
|
+
* Monitors cascade convergence by tracking a Lyapunov-like function:
|
|
5
|
+
* V(t) = weighted sum of error metrics
|
|
6
|
+
*
|
|
7
|
+
* ΔV < 0 → converging (good)
|
|
8
|
+
* ΔV alternating signs → oscillating (reduce Kp by 0.7×)
|
|
9
|
+
* ΔV all positive → diverging (emergency synthesis)
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Compute Lyapunov-like function from error components.
|
|
13
|
+
* V(t) = Σ wᵢ × eᵢ² (quadratic form — always non-negative)
|
|
14
|
+
*/
|
|
15
|
+
export function computeLyapunov(errors) {
|
|
16
|
+
return (0.3 * errors.coverageGap ** 2 +
|
|
17
|
+
0.3 * errors.lowConfidence ** 2 +
|
|
18
|
+
0.2 * errors.contradictions ** 2 +
|
|
19
|
+
0.2 * errors.depthGap ** 2);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Analyze stability from a sequence of Lyapunov values.
|
|
23
|
+
* Requires at least 3 data points.
|
|
24
|
+
*/
|
|
25
|
+
export function analyzeStability(lyapunovHistory) {
|
|
26
|
+
if (lyapunovHistory.length < 3) {
|
|
27
|
+
return {
|
|
28
|
+
state: 'insufficient_data',
|
|
29
|
+
lyapunovValues: lyapunovHistory,
|
|
30
|
+
deltas: [],
|
|
31
|
+
trend: 0,
|
|
32
|
+
recommendation: 'Need at least 3 rounds of data for stability analysis.',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// Compute deltas: ΔV = V(t) - V(t-1)
|
|
36
|
+
const deltas = [];
|
|
37
|
+
for (let i = 1; i < lyapunovHistory.length; i++) {
|
|
38
|
+
deltas.push(lyapunovHistory[i] - lyapunovHistory[i - 1]);
|
|
39
|
+
}
|
|
40
|
+
// Overall trend (simple linear regression slope)
|
|
41
|
+
const n = deltas.length;
|
|
42
|
+
const sumX = n * (n - 1) / 2;
|
|
43
|
+
const sumY = deltas.reduce((s, d) => s + d, 0);
|
|
44
|
+
const sumXY = deltas.reduce((s, d, i) => s + i * d, 0);
|
|
45
|
+
const sumX2 = deltas.reduce((s, _, i) => s + i * i, 0);
|
|
46
|
+
const trend = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX || 1);
|
|
47
|
+
// Check recent deltas (last 3)
|
|
48
|
+
const recent = deltas.slice(-3);
|
|
49
|
+
const allNegative = recent.every(d => d < -0.001);
|
|
50
|
+
const allPositive = recent.every(d => d > 0.001);
|
|
51
|
+
const alternating = recent.length >= 2 &&
|
|
52
|
+
recent.slice(1).every((d, i) => Math.sign(d) !== Math.sign(recent[i]));
|
|
53
|
+
let state;
|
|
54
|
+
let recommendation;
|
|
55
|
+
if (allNegative) {
|
|
56
|
+
state = 'converging';
|
|
57
|
+
recommendation = 'Research is converging. Continue current strategy.';
|
|
58
|
+
}
|
|
59
|
+
else if (allPositive) {
|
|
60
|
+
state = 'diverging';
|
|
61
|
+
recommendation = 'DIVERGING — errors growing. Trigger emergency synthesis. Reduce scope, increase validation.';
|
|
62
|
+
}
|
|
63
|
+
else if (alternating) {
|
|
64
|
+
state = 'oscillating';
|
|
65
|
+
recommendation = 'OSCILLATING — reduce Kp by 0.7×. The cascade is flip-flopping between states.';
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Mixed signals — use trend
|
|
69
|
+
if (trend < -0.01) {
|
|
70
|
+
state = 'converging';
|
|
71
|
+
recommendation = 'Generally converging despite some noise. Continue.';
|
|
72
|
+
}
|
|
73
|
+
else if (trend > 0.01) {
|
|
74
|
+
state = 'diverging';
|
|
75
|
+
recommendation = 'Trending toward divergence. Consider narrowing scope.';
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
state = 'converging'; // Flat = stable
|
|
79
|
+
recommendation = 'Stable plateau. May be approaching saturation — check stopping gates.';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
state,
|
|
84
|
+
lyapunovValues: lyapunovHistory,
|
|
85
|
+
deltas,
|
|
86
|
+
trend,
|
|
87
|
+
recommendation,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if the cascade has reached a convergence criterion.
|
|
92
|
+
* Uses both Lyapunov stability AND absolute error threshold.
|
|
93
|
+
*/
|
|
94
|
+
export function hasConverged(lyapunovHistory, absoluteThreshold = 0.05) {
|
|
95
|
+
if (lyapunovHistory.length === 0) {
|
|
96
|
+
return { converged: false, reason: 'No data' };
|
|
97
|
+
}
|
|
98
|
+
const latest = lyapunovHistory[lyapunovHistory.length - 1];
|
|
99
|
+
// Absolute convergence: V(t) below threshold
|
|
100
|
+
if (latest < absoluteThreshold) {
|
|
101
|
+
return { converged: true, reason: `Lyapunov V(t)=${latest.toFixed(4)} < threshold ${absoluteThreshold}` };
|
|
102
|
+
}
|
|
103
|
+
// Rate convergence: ΔV approaching zero
|
|
104
|
+
if (lyapunovHistory.length >= 3) {
|
|
105
|
+
const recentDeltas = [];
|
|
106
|
+
for (let i = lyapunovHistory.length - 3; i < lyapunovHistory.length; i++) {
|
|
107
|
+
if (i > 0)
|
|
108
|
+
recentDeltas.push(Math.abs(lyapunovHistory[i] - lyapunovHistory[i - 1]));
|
|
109
|
+
}
|
|
110
|
+
const avgDelta = recentDeltas.reduce((s, d) => s + d, 0) / recentDeltas.length;
|
|
111
|
+
if (avgDelta < 0.005) {
|
|
112
|
+
return { converged: true, reason: `Average ΔV=${avgDelta.toFixed(5)} — compression progress exhausted` };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { converged: false, reason: `V(t)=${latest.toFixed(4)}, still above threshold` };
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=stability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stability.js","sourceRoot":"","sources":["../../src/control/stability.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite database wrapper for Research Cascade knowledge store.
|
|
3
|
+
* Uses synchronous better-sqlite3 (no async race conditions with JSONL state).
|
|
4
|
+
* WAL mode, single writer + multiple readers.
|
|
5
|
+
*/
|
|
6
|
+
import Database from 'better-sqlite3';
|
|
7
|
+
export interface DatabaseOptions {
|
|
8
|
+
dbPath?: string;
|
|
9
|
+
readonly?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** For testing only — inject an in-memory DB */
|
|
12
|
+
export declare function __setTestDb(db: Database.Database | null): void;
|
|
13
|
+
export declare function getDb(options?: DatabaseOptions): Database.Database;
|
|
14
|
+
export declare function closeDb(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Check WAL size and run passive checkpoint if > threshold.
|
|
17
|
+
* Call periodically (e.g., between cascade rounds).
|
|
18
|
+
*/
|
|
19
|
+
export declare function checkpointIfNeeded(thresholdMB?: number): void;
|
|
20
|
+
/** Generate a content-addressable ID from claim text */
|
|
21
|
+
export declare function contentHash(text: string): string;
|
|
22
|
+
/** Generate a random UUID-like ID */
|
|
23
|
+
export declare function generateId(): string;
|
|
24
|
+
/** Begin an immediate write transaction */
|
|
25
|
+
export declare function withTransaction<T>(fn: () => T): T;
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/db/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite database wrapper for Research Cascade knowledge store.
|
|
3
|
+
* Uses synchronous better-sqlite3 (no async race conditions with JSONL state).
|
|
4
|
+
* WAL mode, single writer + multiple readers.
|
|
5
|
+
*/
|
|
6
|
+
import Database from 'better-sqlite3';
|
|
7
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
8
|
+
import { join, dirname } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { mkdirSync, existsSync } from 'node:fs';
|
|
11
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const SCHEMA_PATH = join(__dirname, '..', '..', 'src', 'db', 'schema.sql');
|
|
15
|
+
const SCHEMA_DIST_PATH = join(__dirname, 'schema.sql');
|
|
16
|
+
let _db = null;
|
|
17
|
+
function getDefaultDbPath() {
|
|
18
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
19
|
+
const dir = join(home, '.cascade-engine');
|
|
20
|
+
if (!existsSync(dir)) {
|
|
21
|
+
mkdirSync(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
return join(dir, 'knowledge.db');
|
|
24
|
+
}
|
|
25
|
+
/** For testing only — inject an in-memory DB */
|
|
26
|
+
export function __setTestDb(db) {
|
|
27
|
+
_db = db;
|
|
28
|
+
}
|
|
29
|
+
export function getDb(options) {
|
|
30
|
+
if (_db)
|
|
31
|
+
return _db;
|
|
32
|
+
const dbPath = options?.dbPath || process.env.CASCADE_DB_PATH || getDefaultDbPath();
|
|
33
|
+
const dbDir = dirname(dbPath);
|
|
34
|
+
if (!existsSync(dbDir)) {
|
|
35
|
+
mkdirSync(dbDir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
_db = new Database(dbPath, {
|
|
38
|
+
readonly: options?.readonly ?? false,
|
|
39
|
+
fileMustExist: false,
|
|
40
|
+
});
|
|
41
|
+
// Critical PRAGMAs — order matters
|
|
42
|
+
_db.pragma('journal_mode = WAL');
|
|
43
|
+
_db.pragma('busy_timeout = 10000');
|
|
44
|
+
_db.pragma('synchronous = NORMAL');
|
|
45
|
+
_db.pragma('cache_size = -64000'); // 64MB
|
|
46
|
+
_db.pragma('foreign_keys = ON');
|
|
47
|
+
_db.pragma('temp_store = MEMORY');
|
|
48
|
+
_db.pragma('mmap_size = 268435456'); // 256MB
|
|
49
|
+
// Run schema migration
|
|
50
|
+
migrateSchema(_db);
|
|
51
|
+
// Register shutdown handler
|
|
52
|
+
process.on('exit', () => closeDb());
|
|
53
|
+
return _db;
|
|
54
|
+
}
|
|
55
|
+
function migrateSchema(db) {
|
|
56
|
+
// Try source path first (dev), then dist path (production)
|
|
57
|
+
let schemaPath = SCHEMA_PATH;
|
|
58
|
+
if (!existsSync(schemaPath)) {
|
|
59
|
+
schemaPath = SCHEMA_DIST_PATH;
|
|
60
|
+
}
|
|
61
|
+
if (!existsSync(schemaPath)) {
|
|
62
|
+
// Inline minimal check — schema.sql should be copied during build
|
|
63
|
+
console.error('[cascade-engine] Warning: schema.sql not found, skipping migration');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const schema = readFileSync(schemaPath, 'utf-8');
|
|
67
|
+
// Split on semicolons, filter empty, execute each statement
|
|
68
|
+
// All CREATE statements use IF NOT EXISTS — safe to re-run
|
|
69
|
+
db.exec(schema);
|
|
70
|
+
}
|
|
71
|
+
export function closeDb() {
|
|
72
|
+
if (_db) {
|
|
73
|
+
try {
|
|
74
|
+
// Optimize before close
|
|
75
|
+
_db.pragma('optimize');
|
|
76
|
+
_db.close();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Already closed or errored — ignore
|
|
80
|
+
}
|
|
81
|
+
_db = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check WAL size and run passive checkpoint if > threshold.
|
|
86
|
+
* Call periodically (e.g., between cascade rounds).
|
|
87
|
+
*/
|
|
88
|
+
export function checkpointIfNeeded(thresholdMB = 50) {
|
|
89
|
+
const db = getDb();
|
|
90
|
+
const walPath = db.name + '-wal';
|
|
91
|
+
try {
|
|
92
|
+
const { size } = statSync(walPath);
|
|
93
|
+
if (size > thresholdMB * 1024 * 1024) {
|
|
94
|
+
db.pragma('wal_checkpoint(PASSIVE)');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// WAL file doesn't exist or can't stat — fine
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// --- Helpers for common patterns ---
|
|
102
|
+
/** Generate a content-addressable ID from claim text */
|
|
103
|
+
export function contentHash(text) {
|
|
104
|
+
return createHash('sha256').update(text).digest('hex').slice(0, 16);
|
|
105
|
+
}
|
|
106
|
+
/** Generate a random UUID-like ID */
|
|
107
|
+
export function generateId() {
|
|
108
|
+
return randomBytes(8).toString('hex');
|
|
109
|
+
}
|
|
110
|
+
/** Begin an immediate write transaction */
|
|
111
|
+
export function withTransaction(fn) {
|
|
112
|
+
const db = getDb();
|
|
113
|
+
const transaction = db.transaction(fn);
|
|
114
|
+
return transaction();
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"}
|