mindforge-cc 7.0.0 → 8.0.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/.mindforge/config.json +50 -10
- package/.planning/AUDIT.jsonl +15 -0
- package/CHANGELOG.md +23 -0
- package/README.md +33 -27
- package/RELEASENOTES.md +31 -0
- package/bin/engine/mesh-syncer.js +129 -0
- package/bin/engine/nexus-tracer.js +23 -1
- package/bin/engine/orbital-guardian.js +84 -0
- package/bin/engine/skill-evolver.js +105 -0
- package/bin/governance/config-manager.js +26 -0
- package/bin/governance/policy-gate-hardened.js +30 -49
- package/bin/memory/semantic-hub.js +107 -5
- package/bin/memory/vector-hub.js +170 -0
- package/bin/migrations/v8-sqlite-migration.js +85 -0
- package/bin/revops/remediation-queue.js +48 -24
- package/docs/INTELLIGENCE-MESH.md +13 -13
- package/docs/usp-features.md +12 -4
- package/package.json +6 -4
|
@@ -46,6 +46,32 @@ class ConfigManager {
|
|
|
46
46
|
return value;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
set(key, value) {
|
|
50
|
+
const keys = key.split('.');
|
|
51
|
+
let target = this.config;
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
54
|
+
const k = keys[i];
|
|
55
|
+
if (!target[k]) target[k] = {};
|
|
56
|
+
target = target[k];
|
|
57
|
+
}
|
|
58
|
+
target[keys[keys.length - 1]] = value;
|
|
59
|
+
|
|
60
|
+
this._save();
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_save() {
|
|
65
|
+
try {
|
|
66
|
+
if (!fs.existsSync(path.dirname(this.configPath))) {
|
|
67
|
+
fs.mkdirSync(path.dirname(this.configPath), { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error(`[ConfigManager] Failed to save config: ${err.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
49
75
|
getAll() {
|
|
50
76
|
return this.config;
|
|
51
77
|
}
|
|
@@ -1,77 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MindForge
|
|
3
|
-
* Component: Hardened Policy Gate
|
|
2
|
+
* MindForge v8 — Orbital Governance (Pillar XVIII)
|
|
3
|
+
* Component: Hardened Policy Gate (Final Evolution)
|
|
4
4
|
*
|
|
5
|
-
* Enforces
|
|
5
|
+
* Enforces hardware-attested bypasses for high-impact system mutations.
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
const
|
|
9
|
+
const orbitalGuardian = require('../engine/orbital-guardian');
|
|
10
|
+
const configManager = require('../governance/config-manager');
|
|
11
11
|
|
|
12
12
|
class PolicyGateHardened {
|
|
13
13
|
constructor() {
|
|
14
|
-
|
|
14
|
+
// bypasses.json deprecated in favor of orbital.attestations table (v8)
|
|
15
|
+
this.criticalThreshold = configManager.get('governance.critical_drift_threshold', 95);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
* Evaluates if an intent requires
|
|
19
|
-
* @param {Object} intent
|
|
20
|
-
* @param {number} impactScore
|
|
19
|
+
* Evaluates if an intent requires hardware-bound attestation.
|
|
21
20
|
*/
|
|
22
21
|
async evaluateBypass(intent, impactScore) {
|
|
23
|
-
if (impactScore <=
|
|
22
|
+
if (impactScore <= this.criticalThreshold) {
|
|
24
23
|
return { status: 'ALLOWED', reason: 'Impact within standard threshold' };
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
console.log(`[
|
|
28
|
-
|
|
29
|
-
// Check if a pre-existing bypass exists for this request
|
|
30
|
-
const bypasses = this._loadBypasses();
|
|
31
|
-
const existing = bypasses.find(b => b.requestId === intent.requestId && b.status === 'APPROVED');
|
|
26
|
+
console.log(`[ORBITAL-GATE] Impact Score ${impactScore} requires Hardware Attestation`);
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
// 1. Check SQLite via OrbitalGuardian (Unified v8 persistence)
|
|
29
|
+
const attestation = await orbitalGuardian.verify(intent.requestId);
|
|
30
|
+
|
|
31
|
+
if (attestation.verified) {
|
|
32
|
+
return {
|
|
33
|
+
status: 'ALLOWED',
|
|
34
|
+
reason: 'Hardware Attestation Verified via Enclave',
|
|
35
|
+
attestation_id: attestation.id,
|
|
36
|
+
timestamp: attestation.timestamp
|
|
37
|
+
};
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
// Trigger
|
|
40
|
+
// 2. Trigger Orbital Challenge
|
|
38
41
|
return {
|
|
39
|
-
status: '
|
|
40
|
-
reason: 'Biometric
|
|
41
|
-
challenge_id: `
|
|
42
|
-
|
|
42
|
+
status: 'WAIT_FOR_ORBITAL',
|
|
43
|
+
reason: 'Hardware/Biometric attestation required for orbital-tier mutation',
|
|
44
|
+
challenge_id: `orb_${Math.random().toString(36).substr(2, 6)}`,
|
|
45
|
+
impact: impactScore
|
|
43
46
|
};
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
|
-
* Records a
|
|
50
|
+
* Records a hardware-attested approval.
|
|
48
51
|
*/
|
|
49
|
-
async recordBypass(requestId,
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
signature,
|
|
54
|
-
status: 'APPROVED',
|
|
55
|
-
timestamp: new Date().toISOString()
|
|
56
|
-
});
|
|
57
|
-
this._saveBypasses(bypasses);
|
|
58
|
-
console.log(`[PQAS-GATE] Recorded Biometric Approval for Request: ${requestId}`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
_loadBypasses() {
|
|
62
|
-
try {
|
|
63
|
-
if (fs.existsSync(this.bypassStore)) {
|
|
64
|
-
return JSON.parse(fs.readFileSync(this.bypassStore, 'utf8'));
|
|
65
|
-
}
|
|
66
|
-
} catch (err) {}
|
|
67
|
-
return [];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
_saveBypasses(data) {
|
|
71
|
-
if (!fs.existsSync(path.dirname(this.bypassStore))) {
|
|
72
|
-
fs.mkdirSync(path.dirname(this.bypassStore), { recursive: true });
|
|
73
|
-
}
|
|
74
|
-
fs.writeFileSync(this.bypassStore, JSON.stringify(data, null, 2));
|
|
52
|
+
async recordBypass(requestId, did, signature_blob = 'MOCK_HARDWARE_SIGN_v8') {
|
|
53
|
+
const report = await orbitalGuardian.attest(requestId, did, signature_blob);
|
|
54
|
+
console.log(`[ORBITAL-GATE] Recorded Hardware Approval for Request: ${requestId}`);
|
|
55
|
+
return report;
|
|
75
56
|
}
|
|
76
57
|
}
|
|
77
58
|
|
|
@@ -6,12 +6,21 @@
|
|
|
6
6
|
const fs = require('node:fs/promises');
|
|
7
7
|
const path = require('node:path');
|
|
8
8
|
const os = require('node:os');
|
|
9
|
+
const vectorHub = require('./vector-hub'); // v8 Pillar XV
|
|
9
10
|
|
|
10
11
|
class SemanticHub {
|
|
11
12
|
constructor() {
|
|
12
13
|
this.localPath = '.mindforge/memory';
|
|
13
14
|
this.globalPath = path.join(os.homedir(), '.mindforge/memory/global');
|
|
14
15
|
this.syncManifest = path.join(this.localPath, 'sync-manifest.json');
|
|
16
|
+
this.vhInitialized = false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async ensureInit() {
|
|
20
|
+
if (!this.vhInitialized) {
|
|
21
|
+
await vectorHub.init();
|
|
22
|
+
this.vhInitialized = true;
|
|
23
|
+
}
|
|
15
24
|
}
|
|
16
25
|
|
|
17
26
|
/**
|
|
@@ -85,23 +94,116 @@ class SemanticHub {
|
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
/**
|
|
88
|
-
* Retrieves all 'golden_trace' types from the global hub.
|
|
97
|
+
* Retrieves all 'golden_trace' types from the global hub and local SQLite.
|
|
89
98
|
*/
|
|
90
99
|
async getGoldenTraces(skillFilter = null) {
|
|
100
|
+
await this.ensureInit();
|
|
101
|
+
|
|
102
|
+
// v8: Prioritize SQLite search for high-speed retrieval
|
|
103
|
+
let sqliteTraces = [];
|
|
104
|
+
try {
|
|
105
|
+
if (skillFilter) {
|
|
106
|
+
sqliteTraces = await vectorHub.searchTraces(skillFilter);
|
|
107
|
+
} else {
|
|
108
|
+
sqliteTraces = await vectorHub.db.selectFrom('traces')
|
|
109
|
+
.selectAll()
|
|
110
|
+
.where('event', '=', 'reasoning_trace')
|
|
111
|
+
.limit(20)
|
|
112
|
+
.execute();
|
|
113
|
+
}
|
|
114
|
+
} catch (err) {
|
|
115
|
+
console.warn(`[SEMANTIC-HUB] SQLite trace lookup failed: ${err.message}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Legacy file-based fallback/global sync
|
|
91
119
|
const patternFile = path.join(this.globalPath, 'pattern-library.jsonl');
|
|
120
|
+
let fileTraces = [];
|
|
92
121
|
try {
|
|
93
122
|
const data = await fs.readFile(patternFile, 'utf8');
|
|
94
|
-
|
|
123
|
+
fileTraces = data.split('\n')
|
|
95
124
|
.filter(Boolean)
|
|
96
125
|
.map(JSON.parse)
|
|
97
126
|
.filter(p => p.type === 'golden-trace' || p.tags?.includes('success'));
|
|
98
127
|
|
|
99
128
|
if (skillFilter) {
|
|
100
|
-
|
|
129
|
+
fileTraces = fileTraces.filter(t => t.skill === skillFilter || t.tags?.includes(skillFilter));
|
|
101
130
|
}
|
|
102
|
-
return traces;
|
|
103
131
|
} catch (e) {
|
|
104
|
-
|
|
132
|
+
// Fallback is silent
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Merge and deduplicate
|
|
136
|
+
const allTraces = [...sqliteTraces, ...fileTraces];
|
|
137
|
+
const uniqueTraces = Array.from(new Map(allTraces.map(t => [t.id || t.trace_id, t])).values());
|
|
138
|
+
|
|
139
|
+
return uniqueTraces;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Retrieves all 'ghost_pattern' types for proactive risk detection.
|
|
144
|
+
*/
|
|
145
|
+
async getGhostPatterns() {
|
|
146
|
+
await this.ensureInit();
|
|
147
|
+
|
|
148
|
+
// 1. Fetch from legacy file-based global hub
|
|
149
|
+
const patternFile = path.join(this.globalPath, 'pattern-library.jsonl');
|
|
150
|
+
let ghostPatterns = [];
|
|
151
|
+
try {
|
|
152
|
+
const data = await fs.readFile(patternFile, 'utf8');
|
|
153
|
+
ghostPatterns = data.split('\n')
|
|
154
|
+
.filter(Boolean)
|
|
155
|
+
.map(JSON.parse)
|
|
156
|
+
.filter(p => p.type === 'ghost-pattern' || p.tags?.includes('failure'));
|
|
157
|
+
} catch (e) {
|
|
158
|
+
// Missing library is handled gracefully
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 2. Fetch from SQLite high-drift traces (v8 specific ghosting)
|
|
162
|
+
try {
|
|
163
|
+
const v8Ghosts = await vectorHub.db.selectFrom('traces')
|
|
164
|
+
.selectAll()
|
|
165
|
+
.where('drift_score', '>', 0.5)
|
|
166
|
+
.limit(20)
|
|
167
|
+
.execute();
|
|
168
|
+
|
|
169
|
+
const v8Mapped = v8Ghosts.map(g => ({
|
|
170
|
+
id: g.id,
|
|
171
|
+
tags: ['v8-drift-risk', 'failure'],
|
|
172
|
+
failureContext: g.content,
|
|
173
|
+
mitigationStrategy: 'Review logic drift in v8 trace logs.'
|
|
174
|
+
}));
|
|
175
|
+
|
|
176
|
+
ghostPatterns = [...ghostPatterns, ...v8Mapped];
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.warn(`[SEMANTIC-HUB] SQLite ghost lookup failed: ${err.message}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return ghostPatterns;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Saves a discovered skill to SQLite.
|
|
186
|
+
*/
|
|
187
|
+
async saveSkill(skill) {
|
|
188
|
+
await this.ensureInit();
|
|
189
|
+
await vectorHub.db.insertInto('skills')
|
|
190
|
+
.values({
|
|
191
|
+
skill_id: skill.id || `sk_${Math.random().toString(36).substr(2, 6)}`,
|
|
192
|
+
name: skill.name,
|
|
193
|
+
description: skill.description || '',
|
|
194
|
+
path: skill.path || '',
|
|
195
|
+
success_rate: skill.success_rate || 0.0,
|
|
196
|
+
last_verified: new Date().toISOString()
|
|
197
|
+
})
|
|
198
|
+
.onConflict(oc => oc.column('skill_id').doUpdateSet({
|
|
199
|
+
success_rate: skill.success_rate,
|
|
200
|
+
last_verified: new Date().toISOString()
|
|
201
|
+
}))
|
|
202
|
+
.execute();
|
|
203
|
+
|
|
204
|
+
// v8 Pillar XVII: Metadata provenance for evolved skills
|
|
205
|
+
if (skill.is_autonomous) {
|
|
206
|
+
console.log(`[SEMANTIC-HUB] Persistence acknowledged for ASE evolved skill: ${skill.name}`);
|
|
105
207
|
}
|
|
106
208
|
}
|
|
107
209
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const Database = require('better-sqlite3');
|
|
2
|
+
const { Kysely, SqliteDialect, sql } = require('kysely');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MindForge v8 VectorHub
|
|
8
|
+
* Unified Persistence Layer for Trace, Remediation, and Skill data.
|
|
9
|
+
*/
|
|
10
|
+
class VectorHub {
|
|
11
|
+
constructor(dbPath = null) {
|
|
12
|
+
this.dbPath = dbPath || path.join(process.cwd(), '.mindforge', 'celestial.db');
|
|
13
|
+
this._ensureDir();
|
|
14
|
+
|
|
15
|
+
const nativeDb = new Database(this.dbPath);
|
|
16
|
+
this.db = new Kysely({
|
|
17
|
+
dialect: new SqliteDialect({
|
|
18
|
+
database: nativeDb,
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
this.initialized = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_ensureDir() {
|
|
26
|
+
const dir = path.dirname(this.dbPath);
|
|
27
|
+
if (!fs.existsSync(dir)) {
|
|
28
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initialize tables and FTS5 search.
|
|
34
|
+
*/
|
|
35
|
+
async init() {
|
|
36
|
+
if (this.initialized) return;
|
|
37
|
+
|
|
38
|
+
// Traces Table
|
|
39
|
+
await this.db.schema
|
|
40
|
+
.createTable('traces')
|
|
41
|
+
.ifNotExists()
|
|
42
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
43
|
+
.addColumn('trace_id', 'text', (col) => col.notNull())
|
|
44
|
+
.addColumn('span_id', 'text')
|
|
45
|
+
.addColumn('event', 'text', (col) => col.notNull())
|
|
46
|
+
.addColumn('timestamp', 'text', (col) => col.notNull())
|
|
47
|
+
.addColumn('agent', 'text')
|
|
48
|
+
.addColumn('content', 'text')
|
|
49
|
+
.addColumn('metadata', 'text') // JSON blob
|
|
50
|
+
.addColumn('drift_score', 'real')
|
|
51
|
+
.addColumn('mesh_node_id', 'text') // v8 Pillar XVI
|
|
52
|
+
.execute();
|
|
53
|
+
|
|
54
|
+
// v8 Migration: ensure mesh_node_id exists on existing table
|
|
55
|
+
try {
|
|
56
|
+
await sql`ALTER TABLE traces ADD COLUMN mesh_node_id TEXT`.execute(this.db);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
// Column might already exist, ignore error.
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Remediations Table
|
|
62
|
+
await this.db.schema
|
|
63
|
+
.createTable('remediations')
|
|
64
|
+
.ifNotExists()
|
|
65
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
66
|
+
.addColumn('trace_id', 'text', (col) => col.notNull())
|
|
67
|
+
.addColumn('strategy', 'text', (col) => col.notNull())
|
|
68
|
+
.addColumn('status', 'text', (col) => col.notNull())
|
|
69
|
+
.addColumn('timestamp', 'text', (col) => col.notNull())
|
|
70
|
+
.addColumn('outcome', 'text')
|
|
71
|
+
.execute();
|
|
72
|
+
|
|
73
|
+
// Skills Table
|
|
74
|
+
await this.db.schema
|
|
75
|
+
.createTable('skills')
|
|
76
|
+
.ifNotExists()
|
|
77
|
+
.addColumn('skill_id', 'text', (col) => col.primaryKey())
|
|
78
|
+
.addColumn('name', 'text', (col) => col.notNull())
|
|
79
|
+
.addColumn('description', 'text')
|
|
80
|
+
.addColumn('path', 'text')
|
|
81
|
+
.addColumn('success_rate', 'real', (col) => col.defaultTo(0.0))
|
|
82
|
+
.addColumn('last_verified', 'text')
|
|
83
|
+
.execute();
|
|
84
|
+
|
|
85
|
+
// Attestations Table (v8 Pillar XVIII)
|
|
86
|
+
await this.db.schema
|
|
87
|
+
.createTable('attestations')
|
|
88
|
+
.ifNotExists()
|
|
89
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
90
|
+
.addColumn('request_id', 'text', (col) => col.notNull())
|
|
91
|
+
.addColumn('status', 'text', (col) => col.notNull()) // APPROVED / REJECTED
|
|
92
|
+
.addColumn('attestation_payload', 'text') // Signed blob from hardware enclave
|
|
93
|
+
.addColumn('timestamp', 'text', (col) => col.notNull())
|
|
94
|
+
.execute();
|
|
95
|
+
|
|
96
|
+
// Config Table
|
|
97
|
+
await this.db.schema
|
|
98
|
+
.createTable('mesh_config')
|
|
99
|
+
.ifNotExists()
|
|
100
|
+
.addColumn('key', 'text', (col) => col.primaryKey())
|
|
101
|
+
.addColumn('value', 'text')
|
|
102
|
+
.execute();
|
|
103
|
+
|
|
104
|
+
// Enable Full-Text Search for traces (FTS5)
|
|
105
|
+
await sql`
|
|
106
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS traces_search
|
|
107
|
+
USING fts5(trace_id, content, agent, tokenize='porter');
|
|
108
|
+
`.execute(this.db);
|
|
109
|
+
|
|
110
|
+
this.initialized = true;
|
|
111
|
+
console.log(`[VectorHub] Initialized SQLite persistence at ${this.dbPath}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async close() {
|
|
115
|
+
await this.db.destroy();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Record a trace event.
|
|
120
|
+
*/
|
|
121
|
+
async recordTrace(data) {
|
|
122
|
+
const entry = {
|
|
123
|
+
id: data.id || Math.random().toString(36).substr(2, 9),
|
|
124
|
+
trace_id: data.trace_id,
|
|
125
|
+
span_id: data.span_id || null,
|
|
126
|
+
event: data.event,
|
|
127
|
+
timestamp: data.timestamp || new Date().toISOString(),
|
|
128
|
+
agent: data.agent || null,
|
|
129
|
+
content: data.content || null,
|
|
130
|
+
metadata: data.metadata ? JSON.stringify(data.metadata) : null,
|
|
131
|
+
drift_score: data.drift_score || 0,
|
|
132
|
+
mesh_node_id: data.mesh_node_id || null
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
await this.db.insertInto('traces')
|
|
136
|
+
.values(entry)
|
|
137
|
+
.onConflict(oc => oc.column('id').doUpdateSet({
|
|
138
|
+
metadata: entry.metadata,
|
|
139
|
+
mesh_node_id: entry.mesh_node_id,
|
|
140
|
+
drift_score: entry.drift_score
|
|
141
|
+
}))
|
|
142
|
+
.execute();
|
|
143
|
+
|
|
144
|
+
// Update FTS5 index if content exists
|
|
145
|
+
if (entry.content) {
|
|
146
|
+
await sql`INSERT INTO traces_search (trace_id, content, agent) VALUES (${entry.trace_id}, ${entry.content}, ${entry.agent})`
|
|
147
|
+
.execute(this.db);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return entry.id;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Semantic search for previous traces.
|
|
155
|
+
*/
|
|
156
|
+
async searchTraces(query) {
|
|
157
|
+
const results = await sql`
|
|
158
|
+
SELECT t.*, ts.rank
|
|
159
|
+
FROM traces t
|
|
160
|
+
JOIN traces_search ts ON t.trace_id = ts.trace_id
|
|
161
|
+
WHERE traces_search MATCH ${query}
|
|
162
|
+
ORDER BY ts.rank
|
|
163
|
+
LIMIT 10
|
|
164
|
+
`.execute(this.db);
|
|
165
|
+
return results.rows;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = new VectorHub();
|
|
170
|
+
module.exports.VectorHub = VectorHub;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const vectorHub = require('../memory/vector-hub');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* MindForge v8 SQLite Migration Script
|
|
7
|
+
* Migrates legacy .json and .jsonl state into VectorHub.
|
|
8
|
+
*/
|
|
9
|
+
async function runMigration() {
|
|
10
|
+
console.log('[MIGRATION] Starting MindForge v8 (Celestial) SQLite migration...');
|
|
11
|
+
|
|
12
|
+
await vectorHub.init();
|
|
13
|
+
|
|
14
|
+
// 1. Migrate Remediation Queue
|
|
15
|
+
const remPath = path.join(process.cwd(), '.mindforge', 'remediation-queue.json');
|
|
16
|
+
if (fs.existsSync(remPath)) {
|
|
17
|
+
try {
|
|
18
|
+
const data = JSON.parse(fs.readFileSync(remPath, 'utf8'));
|
|
19
|
+
console.log(`[MIGRATION] Migrating ${data.length} remediations...`);
|
|
20
|
+
for (const rem of data) {
|
|
21
|
+
await vectorHub.db.insertInto('remediations')
|
|
22
|
+
.values({
|
|
23
|
+
id: rem.remediation_id,
|
|
24
|
+
trace_id: rem.span_id, // Mapping span_id to trace_id for legacy compatibility
|
|
25
|
+
strategy: rem.strategy,
|
|
26
|
+
status: rem.status,
|
|
27
|
+
timestamp: rem.timestamp || new Date().toISOString(),
|
|
28
|
+
outcome: rem.status === 'SUCCESS' ? 'Legacy Success' : null
|
|
29
|
+
})
|
|
30
|
+
.onConflict(oc => oc.column('id').doNothing())
|
|
31
|
+
.execute();
|
|
32
|
+
}
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.warn(`[MIGRATION] Failed to migrate remediations: ${err.message}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 2. Migrate Audit Traces (AUDIT.jsonl)
|
|
39
|
+
const auditPath = path.join(process.cwd(), '.planning', 'AUDIT.jsonl');
|
|
40
|
+
if (fs.existsSync(auditPath)) {
|
|
41
|
+
try {
|
|
42
|
+
const lines = fs.readFileSync(auditPath, 'utf8').split('\n').filter(Boolean);
|
|
43
|
+
console.log(`[MIGRATION] Migrating ${lines.length} audit trace lines...`);
|
|
44
|
+
|
|
45
|
+
for (const line of lines) {
|
|
46
|
+
const entry = JSON.parse(line);
|
|
47
|
+
if (entry.event === 'reasoning_trace' || entry.event === 'drift_remediation_event') {
|
|
48
|
+
await vectorHub.recordTrace({
|
|
49
|
+
id: entry.id,
|
|
50
|
+
trace_id: entry.trace_id || 'legacy_trace',
|
|
51
|
+
span_id: entry.span_id || null,
|
|
52
|
+
event: entry.event,
|
|
53
|
+
timestamp: entry.timestamp,
|
|
54
|
+
agent: entry.agent || null,
|
|
55
|
+
content: entry.thought || entry.strategy || null,
|
|
56
|
+
metadata: entry,
|
|
57
|
+
drift_score: entry.drift_score || 0
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.warn(`[MIGRATION] Failed to migrate audit traces: ${err.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 3. Set Mesh Node Identity
|
|
67
|
+
await vectorHub.db.insertInto('mesh_config')
|
|
68
|
+
.values({ key: 'mesh_node_id', value: `mindforge-node-${Math.random().toString(36).substr(2, 6)}` })
|
|
69
|
+
.onConflict(oc => oc.column('key').doNothing())
|
|
70
|
+
.execute();
|
|
71
|
+
|
|
72
|
+
await vectorHub.db.insertInto('mesh_config')
|
|
73
|
+
.values({ key: 'v8_migration_complete', value: new Date().toISOString() })
|
|
74
|
+
.onConflict(oc => oc.column('key').doNothing())
|
|
75
|
+
.execute();
|
|
76
|
+
|
|
77
|
+
console.log('[MIGRATION] MindForge v8 Migration Successful.');
|
|
78
|
+
await vectorHub.close();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (require.main === module) {
|
|
82
|
+
runMigration().catch(console.error);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = runMigration;
|
|
@@ -9,10 +9,18 @@
|
|
|
9
9
|
const fs = require('node:fs');
|
|
10
10
|
const path = require('node:path');
|
|
11
11
|
|
|
12
|
+
const vectorHub = require('../memory/vector-hub');
|
|
13
|
+
|
|
12
14
|
class RemediationQueue {
|
|
13
15
|
constructor() {
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
+
this.initialized = false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async ensureInit() {
|
|
20
|
+
if (!this.initialized) {
|
|
21
|
+
await vectorHub.init();
|
|
22
|
+
this.initialized = true;
|
|
23
|
+
}
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
/**
|
|
@@ -34,49 +42,65 @@ class RemediationQueue {
|
|
|
34
42
|
* Adds a new task to the remediation queue.
|
|
35
43
|
*/
|
|
36
44
|
async enqueue(task) {
|
|
45
|
+
await this.ensureInit();
|
|
37
46
|
const entry = {
|
|
38
47
|
...task,
|
|
39
48
|
enqueued_at: new Date().toISOString(),
|
|
40
49
|
status: 'PENDING'
|
|
41
50
|
};
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
await vectorHub.db.insertInto('remediations')
|
|
53
|
+
.values({
|
|
54
|
+
id: entry.remediation_id,
|
|
55
|
+
trace_id: entry.span_id || 'unknown',
|
|
56
|
+
strategy: entry.strategy,
|
|
57
|
+
status: entry.status,
|
|
58
|
+
timestamp: entry.enqueued_at
|
|
59
|
+
})
|
|
60
|
+
.execute();
|
|
61
|
+
|
|
45
62
|
return entry;
|
|
46
63
|
}
|
|
47
64
|
|
|
48
65
|
/**
|
|
49
66
|
* Updates the status of a specific remediation task.
|
|
50
67
|
*/
|
|
51
|
-
updateStatus(remediationId, status) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
async updateStatus(remediationId, status) {
|
|
69
|
+
await this.ensureInit();
|
|
70
|
+
await vectorHub.db.updateTable('remediations')
|
|
71
|
+
.set({
|
|
72
|
+
status,
|
|
73
|
+
outcome: `Updated at ${new Date().toISOString()}`
|
|
74
|
+
})
|
|
75
|
+
.where('id', '=', remediationId)
|
|
76
|
+
.execute();
|
|
58
77
|
}
|
|
59
78
|
|
|
60
79
|
/**
|
|
61
|
-
*
|
|
80
|
+
* Legacy persistence removed in v8 (SQLite transition).
|
|
62
81
|
*/
|
|
63
82
|
_persist() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
console.error(`[RemediationQueue] Failed to persist queue: ${err.message}`);
|
|
71
|
-
}
|
|
83
|
+
// No-op for v8
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_loadQueue() {
|
|
87
|
+
// No-op for v8
|
|
88
|
+
return [];
|
|
72
89
|
}
|
|
73
90
|
|
|
74
|
-
getPending() {
|
|
75
|
-
|
|
91
|
+
async getPending() {
|
|
92
|
+
await this.ensureInit();
|
|
93
|
+
return await vectorHub.db.selectFrom('remediations')
|
|
94
|
+
.selectAll()
|
|
95
|
+
.where('status', '=', 'PENDING')
|
|
96
|
+
.execute();
|
|
76
97
|
}
|
|
77
98
|
|
|
78
|
-
getAll() {
|
|
79
|
-
|
|
99
|
+
async getAll() {
|
|
100
|
+
await this.ensureInit();
|
|
101
|
+
return await vectorHub.db.selectFrom('remediations')
|
|
102
|
+
.selectAll()
|
|
103
|
+
.execute();
|
|
80
104
|
}
|
|
81
105
|
}
|
|
82
106
|
|