mindforge-cc 8.1.1 → 9.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/.claude/settings.local.json +16 -0
- package/.mindforge/celestial.db +0 -0
- package/.mindforge/config.json +7 -7
- package/.planning/REQUIREMENTS.md +13 -1
- package/.planning/STATE.md +13 -8
- package/.planning/jira-sync.json +1 -5
- package/.planning/slack-threads.json +1 -3
- package/CHANGELOG.md +39 -0
- package/LICENSE +1 -1
- package/MINDFORGE.md +14 -13
- package/README.md +9 -2
- package/RELEASENOTES.md +55 -0
- package/bin/autonomous/auto-runner.js +307 -14
- package/bin/engine/learning-manager.js +4 -2
- package/bin/governance/impact-analyzer.js +4 -2
- package/bin/installer-core.js +18 -2
- package/bin/memory/vector-hub.js +148 -16
- package/bin/migrations/migrate.js +1 -0
- package/bin/migrations/v9-unified-memory.js +98 -0
- package/bin/models/cloud-broker.js +7 -6
- package/bin/models/model-broker.js +6 -5
- package/bin/models/model-client.js +12 -8
- package/bin/models/model-router.js +13 -11
- package/bin/sre/adversarial-sre.js +109 -0
- package/bin/sre/sentinel.js +128 -0
- package/bin/sre/shadow-mirror.js +122 -0
- package/bin/sre/sli-verifier.js +81 -0
- package/docs/Context/Master-Context.md +39 -2
- package/docs/PERSONAS.md +40 -0
- package/docs/architecture/V8-SRE.md +88 -0
- package/docs/architecture/V9-BEDROCK.md +162 -0
- package/docs/governance-guide.md +52 -17
- package/package.json +2 -2
- package/.mindforge/bypasses.json +0 -8
- package/.mindforge/memory/decision-library.jsonl +0 -0
- package/.mindforge/memory/knowledge-base.jsonl +0 -15
- package/.mindforge/memory/pattern-library.jsonl +0 -1
- package/.mindforge/memory/team-preferences.jsonl +0 -5
- package/.mindforge/remediation-queue.json +0 -47
- package/.planning/AUDIT.jsonl +0 -45
- package/.planning/RISK-AUDIT.jsonl +0 -53
- package/.planning/ROI.jsonl +0 -2
- package/.planning/TEMPORAL-TEST.md +0 -1
- package/.planning/history/36525e1d9da1b674/ARCHITECTURE.md +0 -0
- package/.planning/history/36525e1d9da1b674/HANDOFF.json +0 -8
- package/.planning/history/36525e1d9da1b674/PROJECT.md +0 -33
- package/.planning/history/36525e1d9da1b674/RELEASE-CHECKLIST.md +0 -68
- package/.planning/history/36525e1d9da1b674/REQUIREMENTS.md +0 -0
- package/.planning/history/36525e1d9da1b674/ROADMAP.md +0 -12
- package/.planning/history/36525e1d9da1b674/SNAPSHOT-META.json +0 -18
- package/.planning/history/36525e1d9da1b674/STATE.md +0 -31
- package/.planning/history/36525e1d9da1b674/TEMPORAL-TEST.md +0 -1
- package/.planning/history/36525e1d9da1b674/jira-sync.json +0 -5
- package/.planning/history/36525e1d9da1b674/slack-threads.json +0 -3
- package/.planning/history/test-audit-001/ARCHITECTURE.md +0 -0
- package/.planning/history/test-audit-001/HANDOFF.json +0 -8
- package/.planning/history/test-audit-001/PROJECT.md +0 -33
- package/.planning/history/test-audit-001/RELEASE-CHECKLIST.md +0 -68
- package/.planning/history/test-audit-001/REQUIREMENTS.md +0 -0
- package/.planning/history/test-audit-001/ROADMAP.md +0 -12
- package/.planning/history/test-audit-001/SNAPSHOT-META.json +0 -17
- package/.planning/history/test-audit-001/STATE.md +0 -31
- package/.planning/history/test-audit-001/TEMPORAL-TEST.md +0 -1
- package/.planning/history/test-audit-001/jira-sync.json +0 -5
- package/.planning/history/test-audit-001/slack-threads.json +0 -3
- package/bin/engine/test-ceg.js +0 -59
- package/bin/engine/test-interlock.js +0 -40
- package/bin/engine/test-remediation.js +0 -61
- package/bin/engine/test-rsa.js +0 -64
- package/bin/engine/test-scs.js +0 -57
- package/bin/engine/test-v7-blueprint.js +0 -44
- package/bin/governance/test-config.js +0 -40
- package/bin/governance/test-crypto-pluggable.js +0 -50
- package/bin/governance/test-hardened-gate.js +0 -71
package/bin/memory/vector-hub.js
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
const Database = require('better-sqlite3');
|
|
2
2
|
const { Kysely, SqliteDialect, sql } = require('kysely');
|
|
3
|
+
const crypto = require('crypto');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const fs = require('fs');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* MindForge
|
|
8
|
-
* Unified Persistence Layer
|
|
8
|
+
* MindForge v9 VectorHub
|
|
9
|
+
* Unified Persistence Layer — traces, remediations, skills, knowledge, and graph edges.
|
|
9
10
|
*/
|
|
10
11
|
class VectorHub {
|
|
11
12
|
constructor(dbPath = null) {
|
|
12
13
|
this.dbPath = dbPath || path.join(process.cwd(), '.mindforge', 'celestial.db');
|
|
13
14
|
this._ensureDir();
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
const nativeDb = new Database(this.dbPath);
|
|
16
17
|
this.db = new Kysely({
|
|
17
18
|
dialect: new SqliteDialect({
|
|
18
19
|
database: nativeDb,
|
|
19
20
|
}),
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
this.initialized = false;
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -51,13 +52,6 @@ class VectorHub {
|
|
|
51
52
|
.addColumn('mesh_node_id', 'text') // v8 Pillar XVI
|
|
52
53
|
.execute();
|
|
53
54
|
|
|
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
55
|
// Remediations Table
|
|
62
56
|
await this.db.schema
|
|
63
57
|
.createTable('remediations')
|
|
@@ -101,12 +95,61 @@ class VectorHub {
|
|
|
101
95
|
.addColumn('value', 'text')
|
|
102
96
|
.execute();
|
|
103
97
|
|
|
104
|
-
//
|
|
98
|
+
// v9 Pillar XXVI: Unified Knowledge table (migrated from JSONL stores)
|
|
99
|
+
await this.db.schema
|
|
100
|
+
.createTable('knowledge')
|
|
101
|
+
.ifNotExists()
|
|
102
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
103
|
+
.addColumn('type', 'text', (col) => col.notNull())
|
|
104
|
+
.addColumn('content', 'text', (col) => col.notNull())
|
|
105
|
+
.addColumn('tags', 'text')
|
|
106
|
+
.addColumn('source', 'text')
|
|
107
|
+
.addColumn('confidence', 'real', (col) => col.defaultTo(1.0))
|
|
108
|
+
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
109
|
+
.addColumn('updated_at', 'text')
|
|
110
|
+
.addColumn('metadata', 'text')
|
|
111
|
+
.execute();
|
|
112
|
+
|
|
113
|
+
// v9 Pillar XXVI: Graph edges table (migrated from knowledge-graph JSONL)
|
|
114
|
+
await this.db.schema
|
|
115
|
+
.createTable('graph_edges')
|
|
116
|
+
.ifNotExists()
|
|
117
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
118
|
+
.addColumn('source_id', 'text', (col) => col.notNull())
|
|
119
|
+
.addColumn('target_id', 'text', (col) => col.notNull())
|
|
120
|
+
.addColumn('edge_type', 'text', (col) => col.notNull())
|
|
121
|
+
.addColumn('weight', 'real', (col) => col.defaultTo(1.0))
|
|
122
|
+
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
123
|
+
.execute();
|
|
124
|
+
|
|
125
|
+
// v9 Pillar XXVII: Migration tracking table
|
|
126
|
+
await this.db.schema
|
|
127
|
+
.createTable('_migrations')
|
|
128
|
+
.ifNotExists()
|
|
129
|
+
.addColumn('id', 'integer', (col) => col.primaryKey())
|
|
130
|
+
.addColumn('name', 'text', (col) => col.notNull())
|
|
131
|
+
.addColumn('applied_at', 'text', (col) => col.notNull())
|
|
132
|
+
.execute();
|
|
133
|
+
|
|
134
|
+
// Enable Full-Text Search (FTS5) for traces and knowledge
|
|
105
135
|
await sql`
|
|
106
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS traces_search
|
|
136
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS traces_search
|
|
107
137
|
USING fts5(trace_id, content, agent, tokenize='porter');
|
|
108
138
|
`.execute(this.db);
|
|
109
139
|
|
|
140
|
+
await sql`
|
|
141
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_search
|
|
142
|
+
USING fts5(id, content, tags, tokenize='porter');
|
|
143
|
+
`.execute(this.db);
|
|
144
|
+
|
|
145
|
+
// v9: Secondary indexes for common query patterns
|
|
146
|
+
await sql`CREATE INDEX IF NOT EXISTS idx_traces_trace_id ON traces(trace_id)`.execute(this.db);
|
|
147
|
+
await sql`CREATE INDEX IF NOT EXISTS idx_traces_timestamp ON traces(timestamp)`.execute(this.db);
|
|
148
|
+
await sql`CREATE INDEX IF NOT EXISTS idx_knowledge_type ON knowledge(type)`.execute(this.db);
|
|
149
|
+
await sql`CREATE INDEX IF NOT EXISTS idx_graph_edges_source ON graph_edges(source_id)`.execute(this.db);
|
|
150
|
+
await sql`CREATE INDEX IF NOT EXISTS idx_graph_edges_target ON graph_edges(target_id)`.execute(this.db);
|
|
151
|
+
await sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_migrations_name ON _migrations(name)`.execute(this.db);
|
|
152
|
+
|
|
110
153
|
this.initialized = true;
|
|
111
154
|
console.log(`[VectorHub] Initialized SQLite persistence at ${this.dbPath}`);
|
|
112
155
|
}
|
|
@@ -120,7 +163,7 @@ class VectorHub {
|
|
|
120
163
|
*/
|
|
121
164
|
async recordTrace(data) {
|
|
122
165
|
const entry = {
|
|
123
|
-
id: data.id ||
|
|
166
|
+
id: data.id || crypto.randomBytes(8).toString('hex'),
|
|
124
167
|
trace_id: data.trace_id,
|
|
125
168
|
span_id: data.span_id || null,
|
|
126
169
|
event: data.event,
|
|
@@ -143,6 +186,7 @@ class VectorHub {
|
|
|
143
186
|
|
|
144
187
|
// Update FTS5 index if content exists
|
|
145
188
|
if (entry.content) {
|
|
189
|
+
await sql`DELETE FROM traces_search WHERE trace_id = ${entry.trace_id}`.execute(this.db);
|
|
146
190
|
await sql`INSERT INTO traces_search (trace_id, content, agent) VALUES (${entry.trace_id}, ${entry.content}, ${entry.agent})`
|
|
147
191
|
.execute(this.db);
|
|
148
192
|
}
|
|
@@ -153,9 +197,10 @@ class VectorHub {
|
|
|
153
197
|
/**
|
|
154
198
|
* Semantic search for previous traces.
|
|
155
199
|
*/
|
|
156
|
-
async searchTraces(
|
|
200
|
+
async searchTraces(rawQuery) {
|
|
201
|
+
const query = '"' + rawQuery.replace(/"/g, '""') + '"';
|
|
157
202
|
const results = await sql`
|
|
158
|
-
SELECT t.*, ts.rank
|
|
203
|
+
SELECT t.*, ts.rank
|
|
159
204
|
FROM traces t
|
|
160
205
|
JOIN traces_search ts ON t.trace_id = ts.trace_id
|
|
161
206
|
WHERE traces_search MATCH ${query}
|
|
@@ -164,6 +209,93 @@ class VectorHub {
|
|
|
164
209
|
`.execute(this.db);
|
|
165
210
|
return results.rows;
|
|
166
211
|
}
|
|
212
|
+
// ── v9 Pillar XXVI: Unified Knowledge API ─────────────────────────────────
|
|
213
|
+
|
|
214
|
+
async saveKnowledge(entry) {
|
|
215
|
+
const now = new Date().toISOString();
|
|
216
|
+
const record = {
|
|
217
|
+
id: entry.id || `k_${crypto.randomBytes(8).toString('hex')}`,
|
|
218
|
+
type: entry.type || 'insight',
|
|
219
|
+
content: entry.content,
|
|
220
|
+
tags: Array.isArray(entry.tags) ? entry.tags.join(',') : (entry.tags || ''),
|
|
221
|
+
source: entry.source || 'unknown',
|
|
222
|
+
confidence: entry.confidence ?? 1.0,
|
|
223
|
+
created_at: entry.created_at || now,
|
|
224
|
+
updated_at: now,
|
|
225
|
+
metadata: entry.metadata ? JSON.stringify(entry.metadata) : null,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
await this.db.insertInto('knowledge')
|
|
229
|
+
.values(record)
|
|
230
|
+
.onConflict(oc => oc.column('id').doUpdateSet({
|
|
231
|
+
content: record.content,
|
|
232
|
+
tags: record.tags,
|
|
233
|
+
confidence: record.confidence,
|
|
234
|
+
updated_at: record.updated_at,
|
|
235
|
+
metadata: record.metadata,
|
|
236
|
+
}))
|
|
237
|
+
.execute();
|
|
238
|
+
|
|
239
|
+
await sql`DELETE FROM knowledge_search WHERE id = ${record.id}`.execute(this.db);
|
|
240
|
+
await sql`INSERT INTO knowledge_search (id, content, tags) VALUES (${record.id}, ${record.content}, ${record.tags})`
|
|
241
|
+
.execute(this.db);
|
|
242
|
+
|
|
243
|
+
return record.id;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async searchKnowledge(rawQuery, limit = 10) {
|
|
247
|
+
const query = '"' + rawQuery.replace(/"/g, '""') + '"';
|
|
248
|
+
const results = await sql`
|
|
249
|
+
SELECT k.*, ks.rank
|
|
250
|
+
FROM knowledge k
|
|
251
|
+
JOIN knowledge_search ks ON k.id = ks.id
|
|
252
|
+
WHERE knowledge_search MATCH ${query}
|
|
253
|
+
ORDER BY ks.rank
|
|
254
|
+
LIMIT ${limit}
|
|
255
|
+
`.execute(this.db);
|
|
256
|
+
return results.rows;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async saveEdge(edge) {
|
|
260
|
+
const record = {
|
|
261
|
+
id: edge.id || `e_${crypto.randomBytes(8).toString('hex')}`,
|
|
262
|
+
source_id: edge.source_id,
|
|
263
|
+
target_id: edge.target_id,
|
|
264
|
+
edge_type: edge.edge_type,
|
|
265
|
+
weight: edge.weight ?? 1.0,
|
|
266
|
+
created_at: edge.created_at || new Date().toISOString(),
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
await this.db.insertInto('graph_edges')
|
|
270
|
+
.values(record)
|
|
271
|
+
.onConflict(oc => oc.column('id').doNothing())
|
|
272
|
+
.execute();
|
|
273
|
+
|
|
274
|
+
return record.id;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async getEdges(nodeId) {
|
|
278
|
+
const results = await this.db.selectFrom('graph_edges')
|
|
279
|
+
.selectAll()
|
|
280
|
+
.where((eb) => eb.or([
|
|
281
|
+
eb('source_id', '=', nodeId),
|
|
282
|
+
eb('target_id', '=', nodeId),
|
|
283
|
+
]))
|
|
284
|
+
.execute();
|
|
285
|
+
return results;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ── v9 Pillar XXVII: Migration tracking ──────────────────────────────────
|
|
289
|
+
|
|
290
|
+
async getAppliedMigrations() {
|
|
291
|
+
const rows = await this.db.selectFrom('_migrations').selectAll().execute();
|
|
292
|
+
return rows.map(r => r.name);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async recordMigration(name) {
|
|
296
|
+
await sql`INSERT OR IGNORE INTO _migrations (name, applied_at) VALUES (${name}, ${new Date().toISOString()})`
|
|
297
|
+
.execute(this.db);
|
|
298
|
+
}
|
|
167
299
|
}
|
|
168
300
|
|
|
169
301
|
module.exports = new VectorHub();
|
|
@@ -44,6 +44,7 @@ async function runMigrations(fromVersion, toVersion) {
|
|
|
44
44
|
require('./0.1.0-to-0.5.0'),
|
|
45
45
|
require('./0.5.0-to-0.6.0'),
|
|
46
46
|
require('./0.6.0-to-1.0.0'),
|
|
47
|
+
require('./v9-unified-memory'),
|
|
47
48
|
];
|
|
48
49
|
|
|
49
50
|
// A migration should run if its DESTINATION VERSION falls within the range:
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindForge v9 Migration — Unified Memory Architecture (Pillar XXVI)
|
|
3
|
+
* Migrates knowledge-store JSONL and knowledge-graph JSONL into celestial.db.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const vectorHub = require('../memory/vector-hub');
|
|
10
|
+
|
|
11
|
+
const fromVersion = '8.2.1';
|
|
12
|
+
const toVersion = '9.0.0';
|
|
13
|
+
const description = 'Unified Memory: migrate JSONL knowledge stores into SQLite';
|
|
14
|
+
|
|
15
|
+
async function run() {
|
|
16
|
+
await vectorHub.init();
|
|
17
|
+
|
|
18
|
+
const applied = await vectorHub.getAppliedMigrations();
|
|
19
|
+
if (applied.includes('v9-unified-memory')) {
|
|
20
|
+
console.log('[v9-MIGRATION] Already applied — skipping.');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let knowledgeMigrated = 0;
|
|
25
|
+
let edgesMigrated = 0;
|
|
26
|
+
let skippedLines = 0;
|
|
27
|
+
|
|
28
|
+
await vectorHub.db.transaction().execute(async (trx) => {
|
|
29
|
+
// 1. Migrate knowledge-base.jsonl → knowledge table
|
|
30
|
+
const kbPaths = [
|
|
31
|
+
path.join(process.cwd(), '.mindforge', 'memory', 'knowledge-base.jsonl'),
|
|
32
|
+
path.join(process.cwd(), '.mindforge', 'memory', 'global-knowledge-base.jsonl'),
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
for (const kbPath of kbPaths) {
|
|
36
|
+
if (!fs.existsSync(kbPath)) continue;
|
|
37
|
+
const lines = fs.readFileSync(kbPath, 'utf8').split('\n').filter(Boolean);
|
|
38
|
+
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
try {
|
|
41
|
+
const entry = JSON.parse(line);
|
|
42
|
+
if (entry.status === 'deleted') continue;
|
|
43
|
+
await vectorHub.saveKnowledge({
|
|
44
|
+
id: entry.id,
|
|
45
|
+
type: entry.type || 'insight',
|
|
46
|
+
content: entry.content || entry.insight || '',
|
|
47
|
+
tags: entry.tags || [],
|
|
48
|
+
source: entry.source || (kbPath.includes('global') ? 'global' : 'project'),
|
|
49
|
+
confidence: entry.confidence ?? 1.0,
|
|
50
|
+
created_at: entry.timestamp || entry.created_at,
|
|
51
|
+
metadata: entry,
|
|
52
|
+
});
|
|
53
|
+
knowledgeMigrated++;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
skippedLines++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 2. Migrate knowledge-graph edges → graph_edges table
|
|
61
|
+
const graphPath = path.join(process.cwd(), '.mindforge', 'memory', 'graph-edges.jsonl');
|
|
62
|
+
if (fs.existsSync(graphPath)) {
|
|
63
|
+
const lines = fs.readFileSync(graphPath, 'utf8').split('\n').filter(Boolean);
|
|
64
|
+
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
try {
|
|
67
|
+
const edge = JSON.parse(line);
|
|
68
|
+
await vectorHub.saveEdge({
|
|
69
|
+
id: edge.id || edge.edge_id,
|
|
70
|
+
source_id: edge.source_id || edge.from,
|
|
71
|
+
target_id: edge.target_id || edge.to,
|
|
72
|
+
edge_type: edge.edge_type || edge.type || 'RELATED_TO',
|
|
73
|
+
weight: edge.weight ?? 1.0,
|
|
74
|
+
created_at: edge.timestamp || edge.created_at,
|
|
75
|
+
});
|
|
76
|
+
edgesMigrated++;
|
|
77
|
+
} catch (e) {
|
|
78
|
+
skippedLines++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// 3. Record migration completion (outside transaction — it committed successfully)
|
|
85
|
+
await vectorHub.recordMigration('v9-unified-memory');
|
|
86
|
+
|
|
87
|
+
console.log(`[v9-MIGRATION] Knowledge entries migrated: ${knowledgeMigrated}`);
|
|
88
|
+
console.log(`[v9-MIGRATION] Graph edges migrated: ${edgesMigrated}`);
|
|
89
|
+
if (skippedLines > 0) {
|
|
90
|
+
console.warn(`[v9-MIGRATION] WARNING: ${skippedLines} malformed lines skipped`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (require.main === module) {
|
|
95
|
+
run().then(() => process.exit(0)).catch(err => { console.error(err); process.exit(1); });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { fromVersion, toVersion, description, run };
|
|
@@ -118,12 +118,13 @@ class CloudBroker {
|
|
|
118
118
|
/**
|
|
119
119
|
* Retrieves provider-specific model mapping.
|
|
120
120
|
*/
|
|
121
|
+
// v9: Provider model mappings aligned to Claude 4.x family
|
|
121
122
|
mapToProviderModel(provider, modelGroup) {
|
|
122
123
|
const mappings = {
|
|
123
|
-
'anthropic': { 'sonnet': 'claude-
|
|
124
|
-
'google': { 'sonnet': 'gemini-
|
|
125
|
-
'aws': { 'sonnet': 'anthropic.claude-
|
|
126
|
-
'azure': { 'sonnet': '
|
|
124
|
+
'anthropic': { 'sonnet': 'claude-sonnet-4-6', 'opus': 'claude-opus-4-7', 'haiku': 'claude-haiku-4-5' },
|
|
125
|
+
'google': { 'sonnet': 'gemini-2.5-pro', 'haiku': 'gemini-2.5-flash' },
|
|
126
|
+
'aws': { 'sonnet': 'anthropic.claude-sonnet-4-6-v1:0', 'haiku': 'anthropic.claude-haiku-4-5-v1:0' },
|
|
127
|
+
'azure': { 'sonnet': 'claude-sonnet-4-6', 'haiku': 'claude-haiku-4-5' }
|
|
127
128
|
};
|
|
128
129
|
|
|
129
130
|
return mappings[provider]?.[modelGroup] || mappings[provider]?.['sonnet'];
|
|
@@ -151,9 +152,9 @@ class CloudBroker {
|
|
|
151
152
|
startChaosMode() {
|
|
152
153
|
console.log('[ENTERPRISE-RESILIENCE] CloudBroker Chaos Mode ACTIVE. Simulating jitter and provider dropouts...');
|
|
153
154
|
setInterval(() => {
|
|
154
|
-
const providers = Object.keys(this.
|
|
155
|
+
const providers = Object.keys(this.state);
|
|
155
156
|
const randomProvider = providers[Math.floor(Math.random() * providers.length)];
|
|
156
|
-
this.
|
|
157
|
+
this.state[randomProvider].latency = Math.random() > 0.7 ? 5000 : 100;
|
|
157
158
|
}, 10000);
|
|
158
159
|
}
|
|
159
160
|
}
|
|
@@ -114,14 +114,15 @@ class ModelBroker {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
estimateCost(modelId, input, output) {
|
|
117
|
-
//
|
|
117
|
+
// v9: Pricing aligned to Claude 4.x family (per 1M tokens)
|
|
118
118
|
const rates = {
|
|
119
|
-
'claude-
|
|
120
|
-
'claude-
|
|
121
|
-
'claude-
|
|
119
|
+
'claude-opus-4-7': { in: 15, out: 75 },
|
|
120
|
+
'claude-sonnet-4-6': { in: 3, out: 15 },
|
|
121
|
+
'claude-haiku-4-5': { in: 0.80, out: 4.0 },
|
|
122
|
+
'gemini-2.5-pro': { in: 1.25, out: 10 },
|
|
122
123
|
};
|
|
123
124
|
|
|
124
|
-
const rate = rates[modelId] || rates['claude-
|
|
125
|
+
const rate = rates[modelId] || rates['claude-sonnet-4-6'];
|
|
125
126
|
return (input / 1_000_000) * rate.in + (output / 1_000_000) * rate.out;
|
|
126
127
|
}
|
|
127
128
|
}
|
|
@@ -10,10 +10,11 @@ const AnthropicProvider = require('./anthropic-provider');
|
|
|
10
10
|
const OpenAIProvider = require('./openai-provider');
|
|
11
11
|
const GeminiProvider = require('./gemini-provider');
|
|
12
12
|
|
|
13
|
+
// v9: Fallback chains aligned to Claude 4.x family
|
|
13
14
|
const FALLBACK_CHAINS = {
|
|
14
|
-
'claude-
|
|
15
|
-
'
|
|
16
|
-
'gemini-
|
|
15
|
+
'claude-opus-4-7': ['claude-sonnet-4-6', 'gemini-2.5-pro'],
|
|
16
|
+
'claude-sonnet-4-6': ['claude-haiku-4-5', 'gemini-2.5-pro'],
|
|
17
|
+
'gemini-2.5-pro': ['claude-sonnet-4-6'],
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
class ModelClient {
|
|
@@ -70,24 +71,27 @@ class ModelClient {
|
|
|
70
71
|
return result;
|
|
71
72
|
|
|
72
73
|
} catch (err) {
|
|
73
|
-
|
|
74
|
+
const safeMsg = (err.message || '').replace(/sk-[a-zA-Z0-9_-]+/g, 'sk-***').replace(/key-[a-zA-Z0-9_-]+/g, 'key-***');
|
|
75
|
+
process.stderr.write(`[model-client] ${currentModel} failed: ${safeMsg}\n`);
|
|
74
76
|
if (attempts.indexOf(currentModel) === attempts.length - 1) {
|
|
75
|
-
|
|
77
|
+
const safeErr = new Error(safeMsg);
|
|
78
|
+
safeErr.code = err.code;
|
|
79
|
+
throw safeErr;
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
static _getProvider(modelId) {
|
|
82
|
-
if (modelId.
|
|
86
|
+
if (modelId.startsWith('claude') || modelId.startsWith('anthropic.claude')) {
|
|
83
87
|
if (!process.env.ANTHROPIC_API_KEY) return null;
|
|
84
88
|
return new AnthropicProvider(process.env.ANTHROPIC_API_KEY);
|
|
85
89
|
}
|
|
86
|
-
if (modelId.
|
|
90
|
+
if (modelId.startsWith('gpt') || modelId.startsWith('o1') || modelId.startsWith('o3')) {
|
|
87
91
|
if (!process.env.OPENAI_API_KEY) return null;
|
|
88
92
|
return new OpenAIProvider(process.env.OPENAI_API_KEY);
|
|
89
93
|
}
|
|
90
|
-
if (modelId.
|
|
94
|
+
if (modelId.startsWith('gemini')) {
|
|
91
95
|
if (!process.env.GOOGLE_API_KEY) return null;
|
|
92
96
|
return new GeminiProvider(process.env.GOOGLE_API_KEY);
|
|
93
97
|
}
|
|
@@ -7,18 +7,18 @@
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// v9: Model topology aligned to Claude 4.x family (2026-04)
|
|
11
11
|
const DEFAULTS = {
|
|
12
|
-
PLANNER_MODEL: 'claude-
|
|
13
|
-
EXECUTOR_MODEL: 'claude-
|
|
14
|
-
REVIEWER_MODEL: '
|
|
15
|
-
SECURITY_MODEL: 'claude-
|
|
16
|
-
RESEARCH_MODEL: 'gemini-
|
|
17
|
-
QA_MODEL: 'claude-
|
|
18
|
-
DEBUG_MODEL: 'claude-
|
|
19
|
-
QUICK_MODEL: 'claude-
|
|
20
|
-
CROSS_REVIEW_SECONDARY: '
|
|
21
|
-
CROSS_REVIEW_TERTIARY: 'gemini-
|
|
12
|
+
PLANNER_MODEL: 'claude-opus-4-7',
|
|
13
|
+
EXECUTOR_MODEL: 'claude-sonnet-4-6',
|
|
14
|
+
REVIEWER_MODEL: 'claude-sonnet-4-6',
|
|
15
|
+
SECURITY_MODEL: 'claude-opus-4-7',
|
|
16
|
+
RESEARCH_MODEL: 'gemini-2.5-pro',
|
|
17
|
+
QA_MODEL: 'claude-sonnet-4-6',
|
|
18
|
+
DEBUG_MODEL: 'claude-opus-4-7',
|
|
19
|
+
QUICK_MODEL: 'claude-haiku-4-5',
|
|
20
|
+
CROSS_REVIEW_SECONDARY: 'claude-sonnet-4-6',
|
|
21
|
+
CROSS_REVIEW_TERTIARY: 'gemini-2.5-pro',
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// Persona to setting key mapping
|
|
@@ -31,6 +31,8 @@ const PERSONA_MAP = {
|
|
|
31
31
|
'research-agent': 'RESEARCH_MODEL',
|
|
32
32
|
'debug-specialist': 'DEBUG_MODEL',
|
|
33
33
|
'decision-architect': 'PLANNER_MODEL',
|
|
34
|
+
'sre-engineer': 'EXECUTOR_MODEL',
|
|
35
|
+
'sre-auditor': 'PLANNER_MODEL',
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
let _settingsCache = null;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindForge v9.0.0 — Adversarial SRE Debate (Pillar XXII)
|
|
3
|
+
* Specialized three-way consensus for high-stakes remediation.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const ModelClient = require('../models/model-client');
|
|
10
|
+
const crypto = require('node:crypto');
|
|
11
|
+
|
|
12
|
+
class AdversarialSRE {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.sessionId = options.sessionId || 'sre-session';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Runs the SRE debate loop for a specific incident.
|
|
19
|
+
* @param {Object} incident - The incident details from Sentinel
|
|
20
|
+
* @param {string} mirrorPath - Path to the Shadow Mirror environment
|
|
21
|
+
*/
|
|
22
|
+
async runDebate(incident, mirrorPath) {
|
|
23
|
+
console.log(`⚖️ SRE DEBATE: Starting consensus loop for ${incident.incident_type}...`);
|
|
24
|
+
|
|
25
|
+
// 1. Proposer (SRE Engineer) - Tier 2
|
|
26
|
+
console.log(' [1/3] SRE Engineer proposing fix...');
|
|
27
|
+
const proposerResult = await ModelClient.complete({
|
|
28
|
+
persona: 'sre-engineer',
|
|
29
|
+
tier: 2,
|
|
30
|
+
systemPrompt: `You are the SRE Proposer. An incident of type ${incident.incident_type} was detected.
|
|
31
|
+
Incident Details: ${JSON.stringify(incident.details)}
|
|
32
|
+
Propose a surgical remediation plan that minimizes downtime and avoids regressions.
|
|
33
|
+
Include an [SRE_PLAN] block with specific file mutations.`,
|
|
34
|
+
userMessage: `Environment: ${mirrorPath}\nIncident: ${incident.remediation_id}`,
|
|
35
|
+
taskName: `sre-proposal-${incident.remediation_id}`
|
|
36
|
+
});
|
|
37
|
+
const proposal = proposerResult.content;
|
|
38
|
+
|
|
39
|
+
// 2. Hunter (Chaos/Regression Agent) - Tier 2
|
|
40
|
+
console.log(' [2/3] Chaos Hunter identifying regressions...');
|
|
41
|
+
const hunterResult = await ModelClient.complete({
|
|
42
|
+
persona: 'qa-engineer',
|
|
43
|
+
tier: 2,
|
|
44
|
+
systemPrompt: `You are the SRE Chaos Hunter. Your goal is to find ways the proposed fix could break production.
|
|
45
|
+
Check for resource leaks, performance hits, or new security vulnerabilities introduced by the fix.
|
|
46
|
+
Include an [SRE_CRITIQUE] block with at least 2 critical concerns.`,
|
|
47
|
+
userMessage: `Proposed Fix:\n${proposal}`,
|
|
48
|
+
taskName: `sre-chaos-${incident.remediation_id}`
|
|
49
|
+
});
|
|
50
|
+
const critique = hunterResult.content;
|
|
51
|
+
|
|
52
|
+
// 3. Auditor (Specialist SRE Auditor) - Tier 3 (Opus Locked)
|
|
53
|
+
console.log(' [3/3] SRE Auditor (Opus) rendering final verdict...');
|
|
54
|
+
const auditorResult = await ModelClient.complete({
|
|
55
|
+
persona: 'sre-auditor',
|
|
56
|
+
tier: 3, // Force tier 3 for Opus binding
|
|
57
|
+
systemPrompt: `You are the High-Intelligence SRE Auditor (Opus-class).
|
|
58
|
+
You must analyze the struggle between the Proposer and the Hunter.
|
|
59
|
+
Your goal is to ensure "Deterministic Reliability."
|
|
60
|
+
Render a final [VERDICT]: APPROVED | REJECTED | AMENDED.
|
|
61
|
+
If AMENDED, provide the final corrected logic.`,
|
|
62
|
+
userMessage: `Incident: ${incident.incident_type}\n\nPROPOSAL:\n${proposal}\n\nCRITIQUE:\n${critique}`,
|
|
63
|
+
taskName: `sre-audit-${incident.remediation_id}`
|
|
64
|
+
});
|
|
65
|
+
const finalVerdict = auditorResult.content;
|
|
66
|
+
|
|
67
|
+
// Record the decision
|
|
68
|
+
const decisionId = crypto.randomBytes(4).toString('hex');
|
|
69
|
+
const decisionPath = path.join(process.cwd(), '.planning', 'decisions', `SRE-${decisionId}.md`);
|
|
70
|
+
|
|
71
|
+
const record = `
|
|
72
|
+
# SRE Decision Trace — ${decisionId}
|
|
73
|
+
- **Incident**: ${incident.incident_type}
|
|
74
|
+
- **Timestamp**: ${new Date().toISOString()}
|
|
75
|
+
- **Mirror**: ${mirrorPath}
|
|
76
|
+
|
|
77
|
+
## Proposer Logic
|
|
78
|
+
${proposal}
|
|
79
|
+
|
|
80
|
+
## Chaos Critique
|
|
81
|
+
${critique}
|
|
82
|
+
|
|
83
|
+
## Final Auditor Verdict (Opus)
|
|
84
|
+
${finalVerdict}
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
if (!fs.existsSync(path.dirname(decisionPath))) {
|
|
88
|
+
fs.mkdirSync(path.dirname(decisionPath), { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
fs.writeFileSync(decisionPath, record);
|
|
91
|
+
|
|
92
|
+
console.log(`✅ SRE Debate Complete. Verdict: ${this.parseVerdict(finalVerdict)}`);
|
|
93
|
+
return {
|
|
94
|
+
decision_id: decisionId,
|
|
95
|
+
verdict: this.parseVerdict(finalVerdict),
|
|
96
|
+
trace_path: decisionPath,
|
|
97
|
+
final_logic: finalVerdict
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
parseVerdict(text) {
|
|
102
|
+
if (text.includes('[VERDICT]: APPROVED')) return 'APPROVED';
|
|
103
|
+
if (text.includes('[VERDICT]: REJECTED')) return 'REJECTED';
|
|
104
|
+
if (text.includes('[VERDICT]: AMENDED')) return 'AMENDED';
|
|
105
|
+
return 'UNKNOWN';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = AdversarialSRE;
|