mindforge-cc 5.6.0 → 5.10.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/.agent/CLAUDE.md +16 -7
- package/.mindforge/engine/nexus-tracer.js +7 -111
- package/CHANGELOG.md +16 -0
- package/README.md +48 -7
- package/RELEASENOTES.md +36 -1
- package/bin/dashboard/frontend/index.html +241 -1
- package/bin/dashboard/revops-api.js +47 -0
- package/bin/dashboard/server.js +1 -0
- package/bin/engine/feedback-loop.js +36 -1
- package/bin/engine/nexus-tracer.js +37 -18
- package/bin/engine/sre-manager.js +63 -9
- package/bin/models/cloud-broker.js +89 -11
- package/bin/models/performance-stats.json +22 -0
- package/bin/revops/debt-monitor.js +60 -0
- package/bin/revops/roi-engine.js +60 -0
- package/bin/revops/velocity-forecaster.js +59 -0
- package/docs/INTELLIGENCE-MESH.md +12 -2
- package/docs/architecture/V5-ENTERPRISE.md +25 -22
- package/docs/commands-reference.md +4 -1
- package/docs/governance-guide.md +15 -7
- package/docs/usp-features.md +15 -72
- package/package.json +1 -1
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
* MindForge — WaveFeedbackLoop (Pillar VI: Proactive Equilibrium)
|
|
3
3
|
* Monitors divergence during wave execution and triggers self-healing.
|
|
4
4
|
*/
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
5
7
|
|
|
6
8
|
class WaveFeedbackLoop {
|
|
7
9
|
constructor(config = {}) {
|
|
8
10
|
this.failureThreshold = config.failureThreshold || 0.20; // 20% failure rate
|
|
9
11
|
this.divergenceWeight = config.divergenceWeight || 1.5; // Bias for rapid divergence
|
|
12
|
+
this.statsPath = config.statsPath || path.join(__dirname, '..', 'models', 'performance-stats.json');
|
|
10
13
|
this.waveState = {
|
|
11
14
|
completed: 0,
|
|
12
15
|
failed: 0,
|
|
@@ -16,19 +19,51 @@ class WaveFeedbackLoop {
|
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
|
-
* Updates the feedback loop with a task result.
|
|
22
|
+
* Updates the feedback loop with a task result and records performance stats.
|
|
20
23
|
*/
|
|
21
24
|
update(result) {
|
|
22
25
|
this.waveState.total++;
|
|
26
|
+
const provider = result.providerId || 'unknown';
|
|
27
|
+
const taskType = result.taskType || 'default';
|
|
28
|
+
|
|
23
29
|
if (result.status === 'completed') {
|
|
24
30
|
this.waveState.completed++;
|
|
31
|
+
this.recordPerformance(provider, taskType, true);
|
|
25
32
|
} else if (result.status === 'failed') {
|
|
26
33
|
this.waveState.failed++;
|
|
34
|
+
this.recordPerformance(provider, taskType, false);
|
|
27
35
|
} else {
|
|
28
36
|
this.waveState.skipped++;
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Records performance metrics to persistent storage.
|
|
42
|
+
*/
|
|
43
|
+
recordPerformance(provider, taskType, isSuccess) {
|
|
44
|
+
if (provider === 'unknown') return;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
let stats = {};
|
|
48
|
+
if (fs.existsSync(this.statsPath)) {
|
|
49
|
+
stats = JSON.parse(fs.readFileSync(this.statsPath, 'utf8'));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!stats[provider]) stats[provider] = {};
|
|
53
|
+
if (!stats[provider][taskType]) stats[provider][taskType] = { success: 0, failure: 0 };
|
|
54
|
+
|
|
55
|
+
if (isSuccess) {
|
|
56
|
+
stats[provider][taskType].success++;
|
|
57
|
+
} else {
|
|
58
|
+
stats[provider][taskType].failure++;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fs.writeFileSync(this.statsPath, JSON.stringify(stats, null, 2));
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.warn(`[MCA-WARN] Failed to record performance stats: ${e.message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
32
67
|
/**
|
|
33
68
|
* Calculates the current divergence rate.
|
|
34
69
|
* @returns {number} - 0.0 to 1.0 divergence rate.
|
|
@@ -45,7 +45,7 @@ class NexusTracer {
|
|
|
45
45
|
/**
|
|
46
46
|
* Start a new ART span.
|
|
47
47
|
*/
|
|
48
|
-
startSpan(name, attributes = {}, parentSpanId = null) {
|
|
48
|
+
async startSpan(name, attributes = {}, parentSpanId = null) {
|
|
49
49
|
const spanId = `sp_${crypto.randomBytes(6).toString('hex')}`;
|
|
50
50
|
const startTime = new Date().toISOString();
|
|
51
51
|
|
|
@@ -59,6 +59,8 @@ class NexusTracer {
|
|
|
59
59
|
attributes: {
|
|
60
60
|
...attributes,
|
|
61
61
|
service: 'mindforge-nexus',
|
|
62
|
+
host: require('os').hostname(),
|
|
63
|
+
pid: process.pid
|
|
62
64
|
}
|
|
63
65
|
};
|
|
64
66
|
|
|
@@ -81,7 +83,7 @@ class NexusTracer {
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
// Record span start in AUDIT.jsonl
|
|
84
|
-
this._recordEvent('span_started', {
|
|
86
|
+
await this._recordEvent('span_started', {
|
|
85
87
|
span_id: spanId,
|
|
86
88
|
parent_span_id: parentSpanId,
|
|
87
89
|
span_name: name,
|
|
@@ -95,7 +97,7 @@ class NexusTracer {
|
|
|
95
97
|
/**
|
|
96
98
|
* End an active span.
|
|
97
99
|
*/
|
|
98
|
-
endSpan(spanId, status = 'success', metadata = {}) {
|
|
100
|
+
async endSpan(spanId, status = 'success', metadata = {}) {
|
|
99
101
|
const span = this.activeSpans.get(spanId);
|
|
100
102
|
if (!span) return;
|
|
101
103
|
|
|
@@ -107,7 +109,7 @@ class NexusTracer {
|
|
|
107
109
|
this.sreManager.terminateEnclave(span.attributes.enclave_id);
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
this._recordEvent('span_completed', {
|
|
112
|
+
await this._recordEvent('span_completed', {
|
|
111
113
|
span_id: spanId,
|
|
112
114
|
status,
|
|
113
115
|
...metadata
|
|
@@ -119,19 +121,31 @@ class NexusTracer {
|
|
|
119
121
|
/**
|
|
120
122
|
* Record a Reasoning Trace event (ART granularity).
|
|
121
123
|
*/
|
|
122
|
-
recordReasoning(spanId, agent, thought, resolution = 'none') {
|
|
124
|
+
async recordReasoning(spanId, agent, thought, resolution = 'none') {
|
|
123
125
|
const span = this.activeSpans.get(spanId);
|
|
124
126
|
let sanitizedThought = thought;
|
|
125
127
|
|
|
126
128
|
if (span && span.attributes.enclave_id) {
|
|
127
|
-
|
|
129
|
+
const result = this.sreManager.sanitizeThoughtChain(thought, span.attributes.enclave_id);
|
|
130
|
+
|
|
131
|
+
if (result.status === 'SRE-ISOLATED') {
|
|
132
|
+
// Log the ZK proof instead of the raw thought
|
|
133
|
+
await this._recordEvent('sre_proof_logged', {
|
|
134
|
+
span_id: spanId,
|
|
135
|
+
agent,
|
|
136
|
+
certificate: result,
|
|
137
|
+
resolution
|
|
138
|
+
});
|
|
139
|
+
return; // Skip standard reasoning trace for isolated content
|
|
140
|
+
}
|
|
141
|
+
sanitizedThought = result.content || thought;
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
// v5 Pillar III: PES (Proactive Equilibrium Scoring)
|
|
131
145
|
const entropy = this.calculateEntropy(spanId, sanitizedThought);
|
|
132
146
|
const isStagnant = entropy > this.RES_THRESHOLD;
|
|
133
147
|
|
|
134
|
-
this._recordEvent('reasoning_trace', {
|
|
148
|
+
await this._recordEvent('reasoning_trace', {
|
|
135
149
|
span_id: spanId,
|
|
136
150
|
agent,
|
|
137
151
|
thought: sanitizedThought,
|
|
@@ -145,7 +159,7 @@ class NexusTracer {
|
|
|
145
159
|
const stagnationCount = history.filter(h => h.entropy > this.RES_THRESHOLD).length;
|
|
146
160
|
|
|
147
161
|
if (stagnationCount >= 3) {
|
|
148
|
-
this._recordEvent('vulnerability_detected', {
|
|
162
|
+
await this._recordEvent('vulnerability_detected', {
|
|
149
163
|
span_id: spanId,
|
|
150
164
|
type: 'REASONING_LOOP',
|
|
151
165
|
severity: 'HIGH',
|
|
@@ -154,7 +168,7 @@ class NexusTracer {
|
|
|
154
168
|
});
|
|
155
169
|
|
|
156
170
|
// Signal proactive recovery
|
|
157
|
-
this.recordSelfHeal(spanId, {
|
|
171
|
+
await this.recordSelfHeal(spanId, {
|
|
158
172
|
type: 'PROACTIVE_RCA',
|
|
159
173
|
cause: 'REASONING_STAGNATION',
|
|
160
174
|
suggestion: 'Entropy threshold exceeded. Switch reasoning strategy.'
|
|
@@ -200,7 +214,7 @@ class NexusTracer {
|
|
|
200
214
|
/**
|
|
201
215
|
* Internal AUDIT writer.
|
|
202
216
|
*/
|
|
203
|
-
_recordEvent(event, data) {
|
|
217
|
+
async _recordEvent(event, data) {
|
|
204
218
|
const entry = {
|
|
205
219
|
id: crypto.randomUUID(),
|
|
206
220
|
timestamp: new Date().toISOString(),
|
|
@@ -215,7 +229,7 @@ class NexusTracer {
|
|
|
215
229
|
entry.did = this.did;
|
|
216
230
|
// Sign the stringified entry WITHOUT the signature field itself
|
|
217
231
|
const payload = JSON.stringify(entry);
|
|
218
|
-
entry.signature = ztai.signData(this.did, payload);
|
|
232
|
+
entry.signature = await ztai.signData(this.did, payload);
|
|
219
233
|
} catch (err) {
|
|
220
234
|
console.warn(`[NexusTracer] ZTAI signing failed: ${err.message}`);
|
|
221
235
|
}
|
|
@@ -234,8 +248,8 @@ class NexusTracer {
|
|
|
234
248
|
/**
|
|
235
249
|
* Records a FinOps budget decision (Pillar V).
|
|
236
250
|
*/
|
|
237
|
-
recordFinOps(spanId, decision) {
|
|
238
|
-
this._recordEvent('finops_decision', {
|
|
251
|
+
async recordFinOps(spanId, decision) {
|
|
252
|
+
await this._recordEvent('finops_decision', {
|
|
239
253
|
span_id: spanId,
|
|
240
254
|
...decision
|
|
241
255
|
});
|
|
@@ -244,8 +258,8 @@ class NexusTracer {
|
|
|
244
258
|
/**
|
|
245
259
|
* Records a Self-Healing trigger event (Pillar VI).
|
|
246
260
|
*/
|
|
247
|
-
recordSelfHeal(spanId, report) {
|
|
248
|
-
this._recordEvent('self_heal_trigger', {
|
|
261
|
+
async recordSelfHeal(spanId, report) {
|
|
262
|
+
await this._recordEvent('self_heal_trigger', {
|
|
249
263
|
span_id: spanId,
|
|
250
264
|
...report
|
|
251
265
|
});
|
|
@@ -254,7 +268,7 @@ class NexusTracer {
|
|
|
254
268
|
/**
|
|
255
269
|
* Finalize and export the Agentic SBOM (Pillar IV).
|
|
256
270
|
*/
|
|
257
|
-
exportSBOM(outputPath = null) {
|
|
271
|
+
async exportSBOM(outputPath = null) {
|
|
258
272
|
const finalPath = outputPath || path.join(process.cwd(), '.planning', 'MANIFEST.sbom.json');
|
|
259
273
|
const manifest = {
|
|
260
274
|
...this.sbom,
|
|
@@ -269,7 +283,7 @@ class NexusTracer {
|
|
|
269
283
|
}
|
|
270
284
|
fs.writeFileSync(finalPath, JSON.stringify(manifest, null, 2));
|
|
271
285
|
|
|
272
|
-
this._recordEvent('sbom_exported', { path: finalPath });
|
|
286
|
+
await this._recordEvent('sbom_exported', { path: finalPath });
|
|
273
287
|
return finalPath;
|
|
274
288
|
} catch (err) {
|
|
275
289
|
console.error(`[NexusTracer] Failed to export SBOM: ${err.message}`);
|
|
@@ -278,4 +292,9 @@ class NexusTracer {
|
|
|
278
292
|
}
|
|
279
293
|
}
|
|
280
294
|
|
|
281
|
-
|
|
295
|
+
// Global Singleton Instance for easy mesh-wide access
|
|
296
|
+
const globalTracer = new NexusTracer();
|
|
297
|
+
|
|
298
|
+
// Export both the class and the global instance
|
|
299
|
+
module.exports = globalTracer;
|
|
300
|
+
module.exports.NexusTracer = NexusTracer;
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
const crypto = require('crypto');
|
|
8
8
|
|
|
9
|
+
// Simulated System DID for Enclave Proofs (Tier 3)
|
|
10
|
+
const ENCLAVE_PRIVATE_KEY = 'tier3-enclave-secret-key-sim'; // In production, this would be a TEE-bound private key
|
|
11
|
+
const SYSTEM_DID = 'did:mindforge:enclave:0xbeast';
|
|
12
|
+
|
|
9
13
|
class SREManager {
|
|
10
14
|
constructor() {
|
|
11
15
|
this.activeEnclaves = new Map();
|
|
@@ -26,7 +30,8 @@ class SREManager {
|
|
|
26
30
|
startedAt: new Date().toISOString(),
|
|
27
31
|
principal: context.did,
|
|
28
32
|
hasIP: true,
|
|
29
|
-
isolationLevel: 'Hardware-Enclave (Simulated)'
|
|
33
|
+
isolationLevel: 'Hardware-Enclave (Simulated)',
|
|
34
|
+
cumulativeHash: null // Root of the proof chain
|
|
30
35
|
});
|
|
31
36
|
|
|
32
37
|
console.log(`[SRE-INIT] Initialized Sovereign Reason Enclave: ${enclaveId} for ${context.did}`);
|
|
@@ -34,19 +39,68 @@ class SREManager {
|
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
/**
|
|
37
|
-
* Sanitizes a thought chain
|
|
38
|
-
* Ensures that sensitive IP or "zero-visibility" thoughts are isolated.
|
|
42
|
+
* Sanitizes a thought chain and generates a ZK-Proof Compliance Certificate.
|
|
43
|
+
* Ensures that sensitive IP or "zero-visibility" thoughts are isolated while proving audit-eligibility.
|
|
39
44
|
* @param {string} thoughtChain - The raw agentic thought chain.
|
|
40
|
-
* @
|
|
45
|
+
* @param {string} enclaveId - The active enclave ID.
|
|
46
|
+
* @param {Object} policyResult - Whether the content passed internal policy checks.
|
|
47
|
+
* @returns {Object} - ZK-Proof compliance certificate.
|
|
41
48
|
*/
|
|
42
|
-
sanitizeThoughtChain(thoughtChain, enclaveId) {
|
|
43
|
-
if (!this.activeEnclaves.has(enclaveId))
|
|
49
|
+
sanitizeThoughtChain(thoughtChain, enclaveId, policyResult = { passed: true }) {
|
|
50
|
+
if (!this.activeEnclaves.has(enclaveId)) {
|
|
51
|
+
return { status: 'PLAINTEXT', content: thoughtChain };
|
|
52
|
+
}
|
|
44
53
|
|
|
45
|
-
//
|
|
46
|
-
|
|
54
|
+
// v5 Pillar VI: Merkle-style Cumulative Hash Chain
|
|
55
|
+
const enclaveData = this.activeEnclaves.get(enclaveId);
|
|
56
|
+
const prevHash = enclaveData.cumulativeHash;
|
|
47
57
|
const digest = crypto.createHash('sha256').update(thoughtChain).digest('hex');
|
|
48
58
|
|
|
49
|
-
|
|
59
|
+
// Generate a simulated ZK-Proof Compliance Certificate
|
|
60
|
+
const proofPayload = {
|
|
61
|
+
enclaveId: enclaveId,
|
|
62
|
+
digest: digest,
|
|
63
|
+
prevHash: prevHash, // Links the chain
|
|
64
|
+
policyPassed: policyResult.passed,
|
|
65
|
+
timestamp: new Date().toISOString(),
|
|
66
|
+
principal: enclaveData.principal
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Sign the proof with the Enclave Private Key
|
|
70
|
+
const signature = crypto.createHmac('sha256', ENCLAVE_PRIVATE_KEY)
|
|
71
|
+
.update(JSON.stringify(proofPayload))
|
|
72
|
+
.digest('hex');
|
|
73
|
+
|
|
74
|
+
// Update the cumulative hash for the next block
|
|
75
|
+
const proofHash = crypto.createHash('sha256').update(signature).digest('hex');
|
|
76
|
+
enclaveData.cumulativeHash = proofHash;
|
|
77
|
+
|
|
78
|
+
const certificate = {
|
|
79
|
+
status: 'SRE-ISOLATED',
|
|
80
|
+
proof: proofPayload,
|
|
81
|
+
signature: signature,
|
|
82
|
+
proofHash: proofHash,
|
|
83
|
+
verificationDid: SYSTEM_DID,
|
|
84
|
+
message: `[SRE-ZK-PROOF] Confidential reasoning (sha256:${digest.substring(0, 8)}...) verified by Enclave Auditor.`
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return certificate;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Verifies an SRE Compliance Certificate without seeing the original content.
|
|
92
|
+
*/
|
|
93
|
+
verifyZKProof(certificate) {
|
|
94
|
+
if (certificate.status !== 'SRE-ISOLATED') return false;
|
|
95
|
+
|
|
96
|
+
const expectedSignature = crypto.createHmac('sha256', ENCLAVE_PRIVATE_KEY)
|
|
97
|
+
.update(JSON.stringify(certificate.proof))
|
|
98
|
+
.digest('hex');
|
|
99
|
+
|
|
100
|
+
const isValid = (expectedSignature === certificate.signature);
|
|
101
|
+
const policyPassed = certificate.proof.policyPassed;
|
|
102
|
+
|
|
103
|
+
return isValid && policyPassed;
|
|
50
104
|
}
|
|
51
105
|
|
|
52
106
|
/**
|
|
@@ -3,33 +3,97 @@
|
|
|
3
3
|
* Dynamically routes tasks across multiple cloud providers (Vertex, Bedrock, Azure).
|
|
4
4
|
*/
|
|
5
5
|
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
6
9
|
|
|
7
10
|
class CloudBroker {
|
|
8
11
|
constructor(config = {}) {
|
|
9
12
|
this.providers = config.providers || ['anthropic', 'google', 'aws', 'azure'];
|
|
13
|
+
this.statsPath = config.statsPath || path.join(__dirname, 'performance-stats.json');
|
|
14
|
+
this.blacklist = new Map(); // provider -> expiry (Date)
|
|
15
|
+
this.failureWindow = new Map(); // provider:taskType -> count
|
|
10
16
|
this.state = {
|
|
11
17
|
'anthropic': { latency: 450, costMultiplier: 1.0, healthy: true },
|
|
12
18
|
'google': { latency: 600, costMultiplier: 0.85, healthy: true },
|
|
13
19
|
'aws': { latency: 550, costMultiplier: 0.95, healthy: true },
|
|
14
20
|
'azure': { latency: 650, costMultiplier: 1.1, healthy: true }
|
|
15
21
|
};
|
|
22
|
+
this.reloadStats();
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
/**
|
|
19
|
-
*
|
|
20
|
-
|
|
26
|
+
* Loads performance stats from persistent storage and applies decay (0.95 factor).
|
|
27
|
+
*/
|
|
28
|
+
reloadStats() {
|
|
29
|
+
try {
|
|
30
|
+
if (fs.existsSync(this.statsPath)) {
|
|
31
|
+
const raw = JSON.parse(fs.readFileSync(this.statsPath, 'utf8'));
|
|
32
|
+
|
|
33
|
+
// v5 Pillar V: Metrics Decay (Hyper-Enterprise Hardening)
|
|
34
|
+
// Ensure historical data doesn't anchor the model if recent performance shifts.
|
|
35
|
+
this.performanceStats = {};
|
|
36
|
+
for (const [provider, tasks] of Object.entries(raw)) {
|
|
37
|
+
this.performanceStats[provider] = {};
|
|
38
|
+
for (const [task, stats] of Object.entries(tasks)) {
|
|
39
|
+
this.performanceStats[provider][task] = {
|
|
40
|
+
success: stats.success * 0.95,
|
|
41
|
+
failure: stats.failure * 0.95
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
this.performanceStats = {};
|
|
47
|
+
}
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.warn(`[MCA-WARN] Failed to load performance stats: ${e.message}`);
|
|
50
|
+
this.performanceStats = {};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Selects the optimal provider based on weighted latency, cost, and historical success.
|
|
56
|
+
* @param {Object} requirements - Task requirements (maxLatency, budgetConstraint, taskType)
|
|
21
57
|
* @returns {string} - Best provider ID
|
|
22
58
|
*/
|
|
23
59
|
getBestProvider(requirements = {}) {
|
|
60
|
+
this.reloadStats(); // Just-In-Time refresh for latest feedback loop data
|
|
61
|
+
const taskType = requirements.taskType || 'default';
|
|
62
|
+
|
|
24
63
|
const scored = Object.entries(this.state)
|
|
25
|
-
.filter(([
|
|
64
|
+
.filter(([id, data]) => {
|
|
65
|
+
// v5 Pillar V: Circuit Breaker Verification
|
|
66
|
+
if (!data.healthy) return false;
|
|
67
|
+
const blacklistExpiry = this.blacklist.get(id);
|
|
68
|
+
if (blacklistExpiry && blacklistExpiry > new Date()) {
|
|
69
|
+
return false; // Circuit is OPEN (Blacklisted)
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
})
|
|
26
73
|
.map(([id, data]) => {
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
74
|
+
// Calculate Success Probability for this task
|
|
75
|
+
const stats = this.performanceStats[id]?.[taskType] || { success: 1, failure: 0 };
|
|
76
|
+
const totalTasks = stats.success + stats.failure;
|
|
77
|
+
const successProb = totalTasks > 0 ? (stats.success / totalTasks) : 0.5;
|
|
78
|
+
|
|
79
|
+
// Score Calculation (The "Affinity" Algorithm)
|
|
80
|
+
const latencyWeight = 0.2;
|
|
81
|
+
const costWeight = 0.3;
|
|
82
|
+
const affinityWeight = 0.5;
|
|
83
|
+
|
|
84
|
+
const score = (data.latency * latencyWeight) +
|
|
85
|
+
(data.costMultiplier * 1000 * costWeight) +
|
|
86
|
+
((1.0 - successProb) * 2000 * affinityWeight);
|
|
87
|
+
|
|
88
|
+
return { id, score, successProb: successProb.toFixed(2) };
|
|
30
89
|
});
|
|
31
90
|
|
|
32
91
|
scored.sort((a, b) => a.score - b.score);
|
|
92
|
+
|
|
93
|
+
if (scored.length > 0) {
|
|
94
|
+
console.log(`[MCA-ROUTE] Selected provider: ${scored[0].id} (Affinity: ${scored[0].successProb} for '${taskType}')`);
|
|
95
|
+
}
|
|
96
|
+
|
|
33
97
|
return scored[0]?.id || 'anthropic';
|
|
34
98
|
}
|
|
35
99
|
|
|
@@ -53,20 +117,34 @@ class CloudBroker {
|
|
|
53
117
|
|
|
54
118
|
/**
|
|
55
119
|
* Retrieves provider-specific model mapping.
|
|
56
|
-
* @param {string} provider - Provider ID
|
|
57
|
-
* @param {string} modelGroup - e.g., 'sonnet', 'opus', 'haiku'
|
|
58
120
|
*/
|
|
59
121
|
mapToProviderModel(provider, modelGroup) {
|
|
60
122
|
const mappings = {
|
|
61
123
|
'anthropic': { 'sonnet': 'claude-3-5-sonnet', 'opus': 'claude-3-opus', 'haiku': 'claude-3-haiku' },
|
|
62
|
-
'google': { 'sonnet': 'gemini-1.5-pro', '
|
|
63
|
-
'aws': { 'sonnet': 'anthropic.claude-3-5-sonnet-v2:0', '
|
|
64
|
-
'azure': { 'sonnet': 'gpt-4o', '
|
|
124
|
+
'google': { 'sonnet': 'gemini-1.5-pro', 'haiku': 'gemini-1.5-flash' },
|
|
125
|
+
'aws': { 'sonnet': 'anthropic.claude-3-5-sonnet-v2:0', 'haiku': 'anthropic.claude-3-haiku-v1:0' },
|
|
126
|
+
'azure': { 'sonnet': 'gpt-4o', 'haiku': 'gpt-35-turbo' }
|
|
65
127
|
};
|
|
66
128
|
|
|
67
129
|
return mappings[provider]?.[modelGroup] || mappings[provider]?.['sonnet'];
|
|
68
130
|
}
|
|
69
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Records a task failure and manages the circuit breaker.
|
|
134
|
+
*/
|
|
135
|
+
recordFailure(provider, taskType = 'default') {
|
|
136
|
+
const key = `${provider}:${taskType}`;
|
|
137
|
+
const failures = (this.failureWindow.get(key) || 0) + 1;
|
|
138
|
+
this.failureWindow.set(key, failures);
|
|
139
|
+
|
|
140
|
+
if (failures >= 3) {
|
|
141
|
+
const expiry = new Date(Date.now() + 10 * 60 * 1000); // 10 min blacklist
|
|
142
|
+
this.blacklist.set(provider, expiry);
|
|
143
|
+
console.warn(`[MCA-CIRCUIT-OPEN] Provider '${provider}' blacklisted for 10 min due to consecutive failures on '${taskType}'.`);
|
|
144
|
+
this.failureWindow.set(key, 0); // Reset window upon blacklisting
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
70
148
|
/**
|
|
71
149
|
* Hardening: Simulate provider failures to verify Fallback Protocol.
|
|
72
150
|
*/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"anthropic": {
|
|
3
|
+
"refactor": { "success": 45, "failure": 2 },
|
|
4
|
+
"test": { "success": 30, "failure": 1 },
|
|
5
|
+
"architect": { "success": 12, "failure": 0 }
|
|
6
|
+
},
|
|
7
|
+
"google": {
|
|
8
|
+
"refactor": { "success": 25, "failure": 8 },
|
|
9
|
+
"test": { "success": 50, "failure": 2 },
|
|
10
|
+
"architect": { "success": 8, "failure": 4 }
|
|
11
|
+
},
|
|
12
|
+
"aws": {
|
|
13
|
+
"refactor": { "success": 20, "failure": 5 },
|
|
14
|
+
"test": { "success": 15, "failure": 5 },
|
|
15
|
+
"architect": { "success": 5, "failure": 2 }
|
|
16
|
+
},
|
|
17
|
+
"azure": {
|
|
18
|
+
"refactor": { "success": 10, "failure": 10 },
|
|
19
|
+
"test": { "success": 20, "failure": 5 },
|
|
20
|
+
"architect": { "success": 3, "failure": 5 }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindForge v5.10.0 — AgRevOps Governance Debt Monitor
|
|
3
|
+
* Calculates Security Health Score and Governance Debt.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class DebtMonitor {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.auditPath = path.join(process.cwd(), '.planning', 'AUDIT.jsonl');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Monitor governance debt and security health.
|
|
17
|
+
* @param {Object} metrics - From MetricsAggregator
|
|
18
|
+
*/
|
|
19
|
+
monitor(metrics) {
|
|
20
|
+
const auditEntries = metrics.auditEntries || [];
|
|
21
|
+
|
|
22
|
+
// 1. Identify high-risk events
|
|
23
|
+
const criticalFindings = auditEntries.filter(e => e.event === 'security_finding' && e.severity === 'critical');
|
|
24
|
+
const tier3Approvals = auditEntries.filter(e => e.event === 'approval_granted' && e.tier === 3);
|
|
25
|
+
const policyBypasses = auditEntries.filter(e => e.event === 'policy_bypass');
|
|
26
|
+
|
|
27
|
+
// 2. Calculate Health Score (starts at 100)
|
|
28
|
+
let score = 100;
|
|
29
|
+
score -= (criticalFindings.length * 10);
|
|
30
|
+
score -= (tier3Approvals.length * 5);
|
|
31
|
+
score -= (policyBypasses.length * 15);
|
|
32
|
+
|
|
33
|
+
const healthScore = Math.max(0, score);
|
|
34
|
+
|
|
35
|
+
// 3. Determine status
|
|
36
|
+
let status = 'Excellent';
|
|
37
|
+
if (healthScore < 90) status = 'Good';
|
|
38
|
+
if (healthScore < 75) status = 'Warning';
|
|
39
|
+
if (healthScore < 50) status = 'Critical';
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
security_health_score: healthScore,
|
|
43
|
+
governance_status: status,
|
|
44
|
+
critical_findings: criticalFindings.length,
|
|
45
|
+
tier3_approvals: tier3Approvals.length,
|
|
46
|
+
policy_bypasses: policyBypasses.length,
|
|
47
|
+
debt_level: this.getDebtLevel(healthScore),
|
|
48
|
+
timestamp: new Date().toISOString()
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getDebtLevel(score) {
|
|
53
|
+
if (score >= 95) return 'Minimal';
|
|
54
|
+
if (score >= 80) return 'Managing';
|
|
55
|
+
if (score >= 60) return 'Moderate';
|
|
56
|
+
return 'High';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = new DebtMonitor();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindForge v5.10.0 — AgRevOps ROI Engine
|
|
3
|
+
* Calculates $ Value Saved vs $ Token Cost.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class ROIEngine {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.hourlyRate = 100; // Average Enterprise Dev Hourly Rate (USD)
|
|
13
|
+
this.taskValues = {
|
|
14
|
+
'refactor': 1.5, // 1.5 hours saved
|
|
15
|
+
'test': 0.75, // 0.75 hours saved
|
|
16
|
+
'architect': 4.0, // 4 hours saved
|
|
17
|
+
'default': 0.5 // 0.5 hours saved
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Calculate ROI from current session metrics.
|
|
23
|
+
* @param {Object} metrics - From MetricsAggregator
|
|
24
|
+
*/
|
|
25
|
+
calculate(metrics) {
|
|
26
|
+
const tokenCost = metrics.costs?.reduce((sum, c) => sum + (c.cost || 0), 0) || 0;
|
|
27
|
+
|
|
28
|
+
// map successful tasks to dev hours
|
|
29
|
+
const tasks = metrics.auditEntries?.filter(e => e.event === 'task_completed') || [];
|
|
30
|
+
let hoursSaved = 0;
|
|
31
|
+
|
|
32
|
+
tasks.forEach(t => {
|
|
33
|
+
const type = this.detectTaskType(t.message || t.task || '');
|
|
34
|
+
hoursSaved += this.taskValues[type] || this.taskValues.default;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const grossValue = hoursSaved * this.hourlyRate;
|
|
38
|
+
const netValue = grossValue - tokenCost;
|
|
39
|
+
const roi = tokenCost > 0 ? (netValue / tokenCost) * 100 : 0;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
token_cost: parseFloat(tokenCost.toFixed(4)),
|
|
43
|
+
hours_saved: parseFloat(hoursSaved.toFixed(2)),
|
|
44
|
+
gross_value: parseFloat(grossValue.toFixed(2)),
|
|
45
|
+
net_value: parseFloat(netValue.toFixed(2)),
|
|
46
|
+
roi_percentage: parseFloat(roi.toFixed(1)),
|
|
47
|
+
currency: 'USD'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
detectTaskType(msg) {
|
|
52
|
+
const m = msg.toLowerCase();
|
|
53
|
+
if (m.includes('refactor')) return 'refactor';
|
|
54
|
+
if (m.includes('test') || m.includes('verify')) return 'test';
|
|
55
|
+
if (m.includes('architect') || m.includes('design') || m.includes('pillar')) return 'architect';
|
|
56
|
+
return 'default';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = new ROIEngine();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindForge v5.10.0 — AgRevOps Velocity Forecaster
|
|
3
|
+
* Predicts milestone completion ETA based on historical velocity.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class VelocityForecaster {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.auditPath = path.join(process.cwd(), '.planning', 'AUDIT.jsonl');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Predict completion ETA for remaining tasks.
|
|
17
|
+
* @param {Object} metrics - From MetricsAggregator
|
|
18
|
+
*/
|
|
19
|
+
predict(metrics) {
|
|
20
|
+
const completedTasks = metrics.auditEntries?.filter(e => e.event === 'task_completed') || [];
|
|
21
|
+
if (completedTasks.length < 2) {
|
|
22
|
+
return {
|
|
23
|
+
avg_seconds_per_task: 0,
|
|
24
|
+
tasks_remaining: metrics.tasks_total - metrics.tasks_completed,
|
|
25
|
+
eta: 'Inconclusive (Not enough data)'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Sort by timestamp
|
|
30
|
+
const sorted = [...completedTasks].sort((a,b) => new Date(a.timestamp) - new Date(b.timestamp));
|
|
31
|
+
|
|
32
|
+
// Calculate historical duration between tasks
|
|
33
|
+
let totalSeconds = 0;
|
|
34
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
35
|
+
const delta = (new Date(sorted[i].timestamp) - new Date(sorted[i - 1].timestamp)) / 1000;
|
|
36
|
+
// ignore outliers (gaps longer than 1 hour)
|
|
37
|
+
if (delta < 3600) {
|
|
38
|
+
totalSeconds += delta;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const avgSeconds = totalSeconds / (sorted.length - 1) || 0;
|
|
43
|
+
const remainingTasks = Math.max(0, metrics.tasks_total - metrics.tasks_completed);
|
|
44
|
+
const etaSeconds = avgSeconds * remainingTasks;
|
|
45
|
+
|
|
46
|
+
const etaDate = new Date(Date.now() + (etaSeconds * 1000));
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
avg_seconds_per_task: parseFloat(avgSeconds.toFixed(1)),
|
|
50
|
+
tasks_completed: metrics.tasks_completed,
|
|
51
|
+
tasks_remaining: remainingTasks,
|
|
52
|
+
eta: remainingTasks > 0 ? etaDate.toLocaleString() : 'Done',
|
|
53
|
+
confidence: completedTasks.length > 5 ? 'High (85%)' : 'Medium (60%)',
|
|
54
|
+
timestamp: new Date().toISOString()
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = new VelocityForecaster();
|