@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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cascade/checkpoints.d.ts +55 -0
  3. package/dist/cascade/checkpoints.js +123 -0
  4. package/dist/cascade/checkpoints.js.map +1 -0
  5. package/dist/cascade/engine.d.ts +72 -0
  6. package/dist/cascade/engine.js +170 -0
  7. package/dist/cascade/engine.js.map +1 -0
  8. package/dist/cascade/gates.d.ts +46 -0
  9. package/dist/cascade/gates.js +199 -0
  10. package/dist/cascade/gates.js.map +1 -0
  11. package/dist/cascade/research.d.ts +50 -0
  12. package/dist/cascade/research.js +127 -0
  13. package/dist/cascade/research.js.map +1 -0
  14. package/dist/cli.d.ts +19 -0
  15. package/dist/cli.js +165 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/control/kalman.d.ts +53 -0
  18. package/dist/control/kalman.js +83 -0
  19. package/dist/control/kalman.js.map +1 -0
  20. package/dist/control/pid.d.ts +57 -0
  21. package/dist/control/pid.js +95 -0
  22. package/dist/control/pid.js.map +1 -0
  23. package/dist/control/stability.d.ts +42 -0
  24. package/dist/control/stability.js +117 -0
  25. package/dist/control/stability.js.map +1 -0
  26. package/dist/db/index.d.ts +26 -0
  27. package/dist/db/index.js +116 -0
  28. package/dist/db/index.js.map +1 -0
  29. package/dist/db/schema.sql +282 -0
  30. package/dist/graph/amem.d.ts +80 -0
  31. package/dist/graph/amem.js +190 -0
  32. package/dist/graph/amem.js.map +1 -0
  33. package/dist/graph/entities.d.ts +66 -0
  34. package/dist/graph/entities.js +187 -0
  35. package/dist/graph/entities.js.map +1 -0
  36. package/dist/graph/queries.d.ts +48 -0
  37. package/dist/graph/queries.js +176 -0
  38. package/dist/graph/queries.js.map +1 -0
  39. package/dist/hitl/dashboard.d.ts +51 -0
  40. package/dist/hitl/dashboard.js +135 -0
  41. package/dist/hitl/dashboard.js.map +1 -0
  42. package/dist/hitl/interventions.d.ts +36 -0
  43. package/dist/hitl/interventions.js +150 -0
  44. package/dist/hitl/interventions.js.map +1 -0
  45. package/dist/hitl/steering.d.ts +37 -0
  46. package/dist/hitl/steering.js +118 -0
  47. package/dist/hitl/steering.js.map +1 -0
  48. package/dist/index.d.ts +12 -0
  49. package/dist/index.js +701 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/memory/consolidation.d.ts +51 -0
  52. package/dist/memory/consolidation.js +122 -0
  53. package/dist/memory/consolidation.js.map +1 -0
  54. package/dist/memory/ncd.d.ts +40 -0
  55. package/dist/memory/ncd.js +90 -0
  56. package/dist/memory/ncd.js.map +1 -0
  57. package/dist/memory/sm2.d.ts +44 -0
  58. package/dist/memory/sm2.js +119 -0
  59. package/dist/memory/sm2.js.map +1 -0
  60. package/dist/memory/tiers.d.ts +49 -0
  61. package/dist/memory/tiers.js +145 -0
  62. package/dist/memory/tiers.js.map +1 -0
  63. package/dist/server.d.ts +6 -0
  64. package/dist/server.js +6 -0
  65. package/dist/server.js.map +1 -0
  66. package/dist/trust/ingestion.d.ts +38 -0
  67. package/dist/trust/ingestion.js +147 -0
  68. package/dist/trust/ingestion.js.map +1 -0
  69. package/dist/trust/patterns.d.ts +26 -0
  70. package/dist/trust/patterns.js +78 -0
  71. package/dist/trust/patterns.js.map +1 -0
  72. package/dist/trust/scoring.d.ts +39 -0
  73. package/dist/trust/scoring.js +206 -0
  74. package/dist/trust/scoring.js.map +1 -0
  75. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Obsidicore
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Step-Level Checkpointing
3
+ *
4
+ * Every step is checkpointed independently — crash loses only the current step.
5
+ * Idempotent upserts via content-addressable keys + ON CONFLICT DO UPDATE.
6
+ * Buffer in SQLite, commit to JSONL only after step completes.
7
+ */
8
+ import { CascadeRoundState, CascadePhase } from './engine.js';
9
+ export interface Checkpoint {
10
+ taskId: string;
11
+ roundIndex: number;
12
+ stepIndex: number;
13
+ stepName: string;
14
+ status: 'pending' | 'running' | 'done' | 'failed' | 'skipped';
15
+ stateSnapshot?: string;
16
+ errorMessage?: string;
17
+ createdAt: string;
18
+ completedAt?: string;
19
+ }
20
+ /**
21
+ * Save a checkpoint for the current step.
22
+ */
23
+ export declare function saveCheckpoint(state: CascadeRoundState, status: Checkpoint['status'], errorMessage?: string): void;
24
+ /**
25
+ * Get all checkpoints for a cascade, organized by round.
26
+ */
27
+ export declare function getCheckpoints(cascadeId: string): Record<number, Checkpoint[]>;
28
+ /**
29
+ * Find the last successful checkpoint to resume from.
30
+ */
31
+ export declare function findResumePoint(cascadeId: string): {
32
+ roundIndex: number;
33
+ phase: CascadePhase;
34
+ state: CascadeRoundState | null;
35
+ } | null;
36
+ /**
37
+ * Check idempotency cache for a previously computed result.
38
+ */
39
+ export declare function checkIdempotency(key: string): any | null;
40
+ /**
41
+ * Store a result in the idempotency cache.
42
+ */
43
+ export declare function cacheIdempotent(key: string, result: any, ttlMinutes?: number): void;
44
+ /**
45
+ * Get a compact progress summary for the cascade.
46
+ */
47
+ export declare function getProgressSummary(cascadeId: string): {
48
+ totalRounds: number;
49
+ completedRounds: number;
50
+ currentPhase: string;
51
+ completedSteps: number;
52
+ failedSteps: number;
53
+ lastActivity: string;
54
+ };
55
+ //# sourceMappingURL=checkpoints.d.ts.map
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Step-Level Checkpointing
3
+ *
4
+ * Every step is checkpointed independently — crash loses only the current step.
5
+ * Idempotent upserts via content-addressable keys + ON CONFLICT DO UPDATE.
6
+ * Buffer in SQLite, commit to JSONL only after step completes.
7
+ */
8
+ import { getDb } from '../db/index.js';
9
+ import { PHASE_ORDER } from './engine.js';
10
+ /**
11
+ * Save a checkpoint for the current step.
12
+ */
13
+ export function saveCheckpoint(state, status, errorMessage) {
14
+ const db = getDb();
15
+ const idempotencyKey = `${state.cascadeId}:${state.roundIndex}:${state.stepIndex}:${state.phase}`;
16
+ db.prepare(`INSERT INTO cascade_checkpoints
17
+ (task_id, round_index, step_index, step_name, status, state_snapshot, idempotency_key, error_message)
18
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
19
+ ON CONFLICT(task_id, round_index, step_index) DO UPDATE SET
20
+ status = excluded.status,
21
+ state_snapshot = COALESCE(excluded.state_snapshot, state_snapshot),
22
+ error_message = excluded.error_message,
23
+ completed_at = CASE WHEN excluded.status IN ('done','failed','skipped') THEN datetime('now') ELSE completed_at END`)
24
+ .run(state.cascadeId, state.roundIndex, state.stepIndex, state.phase, status, JSON.stringify(state), idempotencyKey, errorMessage);
25
+ }
26
+ /**
27
+ * Get all checkpoints for a cascade, organized by round.
28
+ */
29
+ export function getCheckpoints(cascadeId) {
30
+ const db = getDb();
31
+ const rows = db.prepare(`SELECT * FROM cascade_checkpoints
32
+ WHERE task_id = ? ORDER BY round_index, step_index`)
33
+ .all(cascadeId);
34
+ const byRound = {};
35
+ for (const row of rows) {
36
+ if (!byRound[row.round_index])
37
+ byRound[row.round_index] = [];
38
+ byRound[row.round_index].push({
39
+ taskId: row.task_id,
40
+ roundIndex: row.round_index,
41
+ stepIndex: row.step_index,
42
+ stepName: row.step_name,
43
+ status: row.status,
44
+ stateSnapshot: row.state_snapshot,
45
+ errorMessage: row.error_message,
46
+ createdAt: row.created_at,
47
+ completedAt: row.completed_at,
48
+ });
49
+ }
50
+ return byRound;
51
+ }
52
+ /**
53
+ * Find the last successful checkpoint to resume from.
54
+ */
55
+ export function findResumePoint(cascadeId) {
56
+ const db = getDb();
57
+ const last = db.prepare(`SELECT * FROM cascade_checkpoints
58
+ WHERE task_id = ? AND status = 'done'
59
+ ORDER BY round_index DESC, step_index DESC LIMIT 1`)
60
+ .get(cascadeId);
61
+ if (!last)
62
+ return null;
63
+ const state = last.state_snapshot ? JSON.parse(last.state_snapshot) : null;
64
+ const phaseIdx = PHASE_ORDER.indexOf(last.step_name);
65
+ const nextPhase = phaseIdx >= 0 && phaseIdx < PHASE_ORDER.length - 1
66
+ ? PHASE_ORDER[phaseIdx + 1]
67
+ : 'identify'; // Start new round
68
+ return {
69
+ roundIndex: phaseIdx >= PHASE_ORDER.length - 1 ? last.round_index + 1 : last.round_index,
70
+ phase: nextPhase,
71
+ state,
72
+ };
73
+ }
74
+ /**
75
+ * Check idempotency cache for a previously computed result.
76
+ */
77
+ export function checkIdempotency(key) {
78
+ const db = getDb();
79
+ // Clean expired entries
80
+ db.prepare("DELETE FROM idempotency_cache WHERE expires_at IS NOT NULL AND expires_at < datetime('now')").run();
81
+ const cached = db.prepare('SELECT result_json FROM idempotency_cache WHERE key = ?').get(key);
82
+ return cached ? JSON.parse(cached.result_json) : null;
83
+ }
84
+ /**
85
+ * Store a result in the idempotency cache.
86
+ */
87
+ export function cacheIdempotent(key, result, ttlMinutes = 60) {
88
+ const db = getDb();
89
+ db.prepare(`INSERT INTO idempotency_cache (key, result_json, expires_at)
90
+ VALUES (?, ?, datetime('now', '+' || ? || ' minutes'))
91
+ ON CONFLICT(key) DO UPDATE SET
92
+ result_json = excluded.result_json,
93
+ expires_at = excluded.expires_at`)
94
+ .run(key, JSON.stringify(result), ttlMinutes);
95
+ }
96
+ /**
97
+ * Get a compact progress summary for the cascade.
98
+ */
99
+ export function getProgressSummary(cascadeId) {
100
+ const db = getDb();
101
+ const cascade = db.prepare('SELECT current_round, max_rounds FROM cascades WHERE id = ?').get(cascadeId);
102
+ if (!cascade)
103
+ throw new Error(`Cascade ${cascadeId} not found`);
104
+ const stats = db.prepare(`SELECT
105
+ COUNT(CASE WHEN status = 'done' THEN 1 END) as completed,
106
+ COUNT(CASE WHEN status = 'failed' THEN 1 END) as failed,
107
+ MAX(COALESCE(completed_at, created_at)) as last_activity
108
+ FROM cascade_checkpoints WHERE task_id = ?`)
109
+ .get(cascadeId);
110
+ const currentStep = db.prepare(`SELECT step_name FROM cascade_checkpoints
111
+ WHERE task_id = ? AND status IN ('running','pending')
112
+ ORDER BY round_index DESC, step_index DESC LIMIT 1`)
113
+ .get(cascadeId);
114
+ return {
115
+ totalRounds: cascade.max_rounds,
116
+ completedRounds: cascade.current_round,
117
+ currentPhase: currentStep?.step_name || 'idle',
118
+ completedSteps: stats?.completed || 0,
119
+ failedSteps: stats?.failed || 0,
120
+ lastActivity: stats?.last_activity || 'never',
121
+ };
122
+ }
123
+ //# sourceMappingURL=checkpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoints.js","sourceRoot":"","sources":["../../src/cascade/checkpoints.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Cascade Engine — Core FSM Loop
3
+ *
4
+ * Implements the research cascade state machine:
5
+ * IDENTIFY → SCREEN → EVALUATE → CONSOLIDATE → MATURE → PRUNE → SYNTHESIZE → OBSERVE → (loop|done)
6
+ *
7
+ * The AGENT orchestrates. This engine TRACKS STATE.
8
+ * The engine provides methods that MCP tools call; the LLM decides when/how to call them.
9
+ */
10
+ export type CascadePhase = 'identify' | 'screen' | 'evaluate' | 'consolidate' | 'mature' | 'prune' | 'synthesize' | 'observe';
11
+ export interface CascadeRoundState {
12
+ cascadeId: string;
13
+ roundIndex: number;
14
+ phase: CascadePhase;
15
+ stepIndex: number;
16
+ explorationBudget: number;
17
+ searchQueries: string[];
18
+ findingsThisRound: number;
19
+ startedAt: string;
20
+ }
21
+ export interface StopCondition {
22
+ name: string;
23
+ met: boolean;
24
+ value: number;
25
+ threshold: number;
26
+ description: string;
27
+ }
28
+ export declare const PHASE_ORDER: CascadePhase[];
29
+ /**
30
+ * Initialize a new round of research.
31
+ * Sets up the round state and calculates exploration budget.
32
+ */
33
+ export declare function initRound(cascadeId: string): CascadeRoundState;
34
+ /**
35
+ * Advance to the next phase in the cascade loop.
36
+ * Returns the new phase, or null if the round is complete.
37
+ */
38
+ export declare function advancePhase(state: CascadeRoundState): CascadePhase | null;
39
+ /**
40
+ * Mark current phase as complete.
41
+ */
42
+ export declare function completePhase(state: CascadeRoundState): void;
43
+ /**
44
+ * Resume from the last successful checkpoint.
45
+ */
46
+ export declare function resumeFromCheckpoint(cascadeId: string): CascadeRoundState | null;
47
+ /**
48
+ * Generate search queries for the IDENTIFY phase.
49
+ * Uses UCB to balance exploration vs exploitation across threads.
50
+ */
51
+ export declare function generateSearchPlan(cascadeId: string): {
52
+ explorationQueries: string[];
53
+ exploitationQueries: string[];
54
+ };
55
+ /**
56
+ * Create a research thread (a sub-question being investigated).
57
+ */
58
+ export declare function createThread(cascadeId: string, question: string, type: 'technical' | 'discovery' | 'classification' | 'validation', agentName?: string, modelUsed?: string): string;
59
+ /**
60
+ * Complete a thread and update its UCB values.
61
+ */
62
+ export declare function completeThread(threadId: string, reward: number): void;
63
+ /**
64
+ * Get round summary for synthesis.
65
+ */
66
+ export declare function getRoundSummary(cascadeId: string, roundIndex: number): {
67
+ findings: any[];
68
+ hypotheses: any[];
69
+ newEntities: number;
70
+ newEdges: number;
71
+ };
72
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Cascade Engine — Core FSM Loop
3
+ *
4
+ * Implements the research cascade state machine:
5
+ * IDENTIFY → SCREEN → EVALUATE → CONSOLIDATE → MATURE → PRUNE → SYNTHESIZE → OBSERVE → (loop|done)
6
+ *
7
+ * The AGENT orchestrates. This engine TRACKS STATE.
8
+ * The engine provides methods that MCP tools call; the LLM decides when/how to call them.
9
+ */
10
+ import { getDb, generateId } from '../db/index.js';
11
+ export const PHASE_ORDER = [
12
+ 'identify', 'screen', 'evaluate', 'consolidate',
13
+ 'mature', 'prune', 'synthesize', 'observe',
14
+ ];
15
+ // --- Engine Functions ---
16
+ /**
17
+ * Initialize a new round of research.
18
+ * Sets up the round state and calculates exploration budget.
19
+ */
20
+ export function initRound(cascadeId) {
21
+ const db = getDb();
22
+ const cascade = db.prepare('SELECT * FROM cascades WHERE id = ?').get(cascadeId);
23
+ if (!cascade)
24
+ throw new Error(`Cascade ${cascadeId} not found`);
25
+ const roundIndex = cascade.current_round;
26
+ const explorationBudget = Math.max(0, 1 - roundIndex / cascade.max_rounds);
27
+ const state = {
28
+ cascadeId,
29
+ roundIndex,
30
+ phase: 'identify',
31
+ stepIndex: 0,
32
+ explorationBudget,
33
+ searchQueries: [],
34
+ findingsThisRound: 0,
35
+ startedAt: new Date().toISOString(),
36
+ };
37
+ // Create checkpoint for round start
38
+ db.prepare(`INSERT INTO cascade_checkpoints (task_id, round_index, step_index, step_name, status, state_snapshot)
39
+ VALUES (?, ?, 0, 'round_init', 'done', ?)
40
+ ON CONFLICT(task_id, round_index, step_index) DO UPDATE SET status = 'done'`)
41
+ .run(cascadeId, roundIndex, JSON.stringify(state));
42
+ return state;
43
+ }
44
+ /**
45
+ * Advance to the next phase in the cascade loop.
46
+ * Returns the new phase, or null if the round is complete.
47
+ */
48
+ export function advancePhase(state) {
49
+ const currentIdx = PHASE_ORDER.indexOf(state.phase);
50
+ if (currentIdx < 0 || currentIdx >= PHASE_ORDER.length - 1)
51
+ return null;
52
+ const nextPhase = PHASE_ORDER[currentIdx + 1];
53
+ state.phase = nextPhase;
54
+ state.stepIndex++;
55
+ // Checkpoint
56
+ const db = getDb();
57
+ db.prepare(`INSERT INTO cascade_checkpoints (task_id, round_index, step_index, step_name, status, state_snapshot)
58
+ VALUES (?, ?, ?, ?, 'running', ?)
59
+ ON CONFLICT(task_id, round_index, step_index) DO UPDATE SET
60
+ status = 'running', state_snapshot = excluded.state_snapshot`)
61
+ .run(state.cascadeId, state.roundIndex, state.stepIndex, nextPhase, JSON.stringify(state));
62
+ return nextPhase;
63
+ }
64
+ /**
65
+ * Mark current phase as complete.
66
+ */
67
+ export function completePhase(state) {
68
+ const db = getDb();
69
+ db.prepare(`UPDATE cascade_checkpoints SET status = 'done', completed_at = datetime('now')
70
+ WHERE task_id = ? AND round_index = ? AND step_index = ?`)
71
+ .run(state.cascadeId, state.roundIndex, state.stepIndex);
72
+ }
73
+ /**
74
+ * Resume from the last successful checkpoint.
75
+ */
76
+ export function resumeFromCheckpoint(cascadeId) {
77
+ const db = getDb();
78
+ // Find the last completed checkpoint
79
+ const last = db.prepare(`SELECT * FROM cascade_checkpoints
80
+ WHERE task_id = ? AND status = 'done'
81
+ ORDER BY round_index DESC, step_index DESC LIMIT 1`)
82
+ .get(cascadeId);
83
+ if (!last?.state_snapshot)
84
+ return null;
85
+ const state = JSON.parse(last.state_snapshot);
86
+ // Advance past the completed step
87
+ const nextPhase = advancePhase(state);
88
+ if (!nextPhase) {
89
+ // Round was complete — start new round
90
+ return initRound(cascadeId);
91
+ }
92
+ return state;
93
+ }
94
+ /**
95
+ * Generate search queries for the IDENTIFY phase.
96
+ * Uses UCB to balance exploration vs exploitation across threads.
97
+ */
98
+ export function generateSearchPlan(cascadeId) {
99
+ const db = getDb();
100
+ const cascade = db.prepare('SELECT * FROM cascades WHERE id = ?').get(cascadeId);
101
+ if (!cascade)
102
+ throw new Error(`Cascade ${cascadeId} not found`);
103
+ const plan = cascade.plan_json ? JSON.parse(cascade.plan_json) : null;
104
+ const questions = plan?.questions || [cascade.question];
105
+ const budget = Math.max(0, 1 - cascade.current_round / cascade.max_rounds);
106
+ // Get existing threads for UCB calculation
107
+ const threads = db.prepare('SELECT * FROM threads WHERE cascade_id = ?').all(cascadeId);
108
+ const totalVisits = threads.reduce((s, t) => s + (t.ucb_visits || 0), 0) || 1;
109
+ // UCB scoring for existing threads
110
+ const C = 1.414; // Exploration constant
111
+ for (const thread of threads) {
112
+ const visits = thread.ucb_visits || 1;
113
+ const reward = thread.ucb_reward || 0;
114
+ thread.ucb_score = reward / visits + C * Math.sqrt(Math.log(totalVisits) / visits);
115
+ }
116
+ // Split budget: exploration gets novel queries, exploitation deepens existing
117
+ const explorationCount = Math.ceil(questions.length * budget);
118
+ const exploitationCount = Math.max(1, questions.length - explorationCount);
119
+ return {
120
+ explorationQueries: questions.slice(0, explorationCount),
121
+ exploitationQueries: threads
122
+ .sort((a, b) => (b.ucb_score || 0) - (a.ucb_score || 0))
123
+ .slice(0, exploitationCount)
124
+ .map((t) => t.question),
125
+ };
126
+ }
127
+ /**
128
+ * Create a research thread (a sub-question being investigated).
129
+ */
130
+ export function createThread(cascadeId, question, type, agentName, modelUsed) {
131
+ const db = getDb();
132
+ const id = generateId();
133
+ db.prepare(`INSERT INTO threads (id, cascade_id, question, type, status, agent_name, model_used, started_at)
134
+ VALUES (?, ?, ?, ?, 'active', ?, ?, datetime('now'))`)
135
+ .run(id, cascadeId, question, type, agentName, modelUsed);
136
+ return id;
137
+ }
138
+ /**
139
+ * Complete a thread and update its UCB values.
140
+ */
141
+ export function completeThread(threadId, reward) {
142
+ const db = getDb();
143
+ db.prepare(`UPDATE threads SET
144
+ status = 'done', completed_at = datetime('now'),
145
+ ucb_visits = ucb_visits + 1,
146
+ ucb_reward = ucb_reward + ?
147
+ WHERE id = ?`)
148
+ .run(reward, threadId);
149
+ }
150
+ /**
151
+ * Get round summary for synthesis.
152
+ */
153
+ export function getRoundSummary(cascadeId, roundIndex) {
154
+ const db = getDb();
155
+ const findings = db.prepare(`SELECT id, claim, confidence, trust_composite, grade_level
156
+ FROM findings WHERE cascade_id = ? AND cascade_round = ? AND quarantined = 0
157
+ ORDER BY confidence DESC`)
158
+ .all(cascadeId, roundIndex);
159
+ const hypotheses = db.prepare(`SELECT id, statement, affinity, status
160
+ FROM hypotheses WHERE cascade_id = ?
161
+ ORDER BY affinity DESC`)
162
+ .all(cascadeId);
163
+ // Approximate new entities/edges this round (by timestamp)
164
+ const newEntities = db.prepare(`SELECT COUNT(*) as n FROM kg_entities
165
+ WHERE created_at >= (SELECT MIN(created_at) FROM cascade_checkpoints WHERE task_id = ? AND round_index = ?)`).get(cascadeId, roundIndex)?.n || 0;
166
+ const newEdges = db.prepare(`SELECT COUNT(*) as n FROM kg_edges
167
+ WHERE created_at >= (SELECT MIN(created_at) FROM cascade_checkpoints WHERE task_id = ? AND round_index = ?)`).get(cascadeId, roundIndex)?.n || 0;
168
+ return { findings, hypotheses, newEntities, newEdges };
169
+ }
170
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/cascade/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Quality Gates — Evaluation between cascade phases
3
+ *
4
+ * Four stopping gates (information-theoretic):
5
+ * 1. Compression plateau — MDL not decreasing
6
+ * 2. Diminishing returns — new findings below threshold
7
+ * 3. Entropy floor — knowledge graph structure stabilized
8
+ * 4. MDL increasing — model growing without explanatory gain
9
+ *
10
+ * Plus inter-phase gates that validate readiness to advance.
11
+ */
12
+ export interface GateResult {
13
+ gate: string;
14
+ passed: boolean;
15
+ value: number;
16
+ threshold: number;
17
+ description: string;
18
+ }
19
+ /**
20
+ * Check all four stopping conditions.
21
+ * If ANY gate says stop, cascade should conclude.
22
+ */
23
+ export declare function evaluateStoppingGates(cascadeId: string): {
24
+ shouldStop: boolean;
25
+ gates: GateResult[];
26
+ recommendation: string;
27
+ };
28
+ /**
29
+ * Validate readiness to advance from one phase to the next.
30
+ * Returns issues that should be resolved before advancing.
31
+ */
32
+ export declare function validatePhaseGate(cascadeId: string, fromPhase: string, toPhase: string): {
33
+ ready: boolean;
34
+ issues: string[];
35
+ };
36
+ /**
37
+ * Calculate information-theoretic metrics for the current round.
38
+ */
39
+ export declare function calculateRoundMetrics(cascadeId: string): {
40
+ coverage: number;
41
+ depth: number;
42
+ confidence: number;
43
+ sourceQuality: number;
44
+ graphDensity: number;
45
+ };
46
+ //# sourceMappingURL=gates.d.ts.map