mindforge-cc 5.4.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.
@@ -34,6 +34,7 @@ try {
34
34
  const SSE = require('./sse-bridge');
35
35
  const API = require('./api-router');
36
36
  const TemporalAPI = require('./temporal-api');
37
+ const RevOpsAPI = require('./revops-api');
37
38
 
38
39
  // ── Express app ───────────────────────────────────────────────────────────────
39
40
  const app = express();
@@ -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.
@@ -21,6 +21,10 @@ class NexusTracer {
21
21
  this.enableZtai = config.enableZtai !== false;
22
22
  this.sreManager = new SREManager();
23
23
 
24
+ // v5 Pillar III: Reasoning Entropy Monitoring (RES)
25
+ this.RES_THRESHOLD = 0.8; // Similarity threshold for stagnation
26
+ this.entropyCache = new Map(); // spanId -> [thoughtHistories]
27
+
24
28
  // v5 Pillar IV: Agentic SBOM
25
29
  this.sbom = {
26
30
  manifest_version: '1.0.0',
@@ -41,7 +45,7 @@ class NexusTracer {
41
45
  /**
42
46
  * Start a new ART span.
43
47
  */
44
- startSpan(name, attributes = {}, parentSpanId = null) {
48
+ async startSpan(name, attributes = {}, parentSpanId = null) {
45
49
  const spanId = `sp_${crypto.randomBytes(6).toString('hex')}`;
46
50
  const startTime = new Date().toISOString();
47
51
 
@@ -55,6 +59,8 @@ class NexusTracer {
55
59
  attributes: {
56
60
  ...attributes,
57
61
  service: 'mindforge-nexus',
62
+ host: require('os').hostname(),
63
+ pid: process.pid
58
64
  }
59
65
  };
60
66
 
@@ -77,7 +83,7 @@ class NexusTracer {
77
83
  }
78
84
 
79
85
  // Record span start in AUDIT.jsonl
80
- this._recordEvent('span_started', {
86
+ await this._recordEvent('span_started', {
81
87
  span_id: spanId,
82
88
  parent_span_id: parentSpanId,
83
89
  span_name: name,
@@ -91,7 +97,7 @@ class NexusTracer {
91
97
  /**
92
98
  * End an active span.
93
99
  */
94
- endSpan(spanId, status = 'success', metadata = {}) {
100
+ async endSpan(spanId, status = 'success', metadata = {}) {
95
101
  const span = this.activeSpans.get(spanId);
96
102
  if (!span) return;
97
103
 
@@ -103,7 +109,7 @@ class NexusTracer {
103
109
  this.sreManager.terminateEnclave(span.attributes.enclave_id);
104
110
  }
105
111
 
106
- this._recordEvent('span_completed', {
112
+ await this._recordEvent('span_completed', {
107
113
  span_id: spanId,
108
114
  status,
109
115
  ...metadata
@@ -115,26 +121,100 @@ class NexusTracer {
115
121
  /**
116
122
  * Record a Reasoning Trace event (ART granularity).
117
123
  */
118
- recordReasoning(spanId, agent, thought, resolution = 'none') {
124
+ async recordReasoning(spanId, agent, thought, resolution = 'none') {
119
125
  const span = this.activeSpans.get(spanId);
120
126
  let sanitizedThought = thought;
121
127
 
122
128
  if (span && span.attributes.enclave_id) {
123
- sanitizedThought = this.sreManager.sanitizeThoughtChain(thought, span.attributes.enclave_id);
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;
124
142
  }
125
143
 
126
- this._recordEvent('reasoning_trace', {
144
+ // v5 Pillar III: PES (Proactive Equilibrium Scoring)
145
+ const entropy = this.calculateEntropy(spanId, sanitizedThought);
146
+ const isStagnant = entropy > this.RES_THRESHOLD;
147
+
148
+ await this._recordEvent('reasoning_trace', {
127
149
  span_id: spanId,
128
150
  agent,
129
151
  thought: sanitizedThought,
130
- resolution
152
+ resolution,
153
+ entropy: parseFloat(entropy.toFixed(4)),
154
+ is_stagnant: isStagnant
131
155
  });
156
+
157
+ if (isStagnant) {
158
+ const history = this.entropyCache.get(spanId) || [];
159
+ const stagnationCount = history.filter(h => h.entropy > this.RES_THRESHOLD).length;
160
+
161
+ if (stagnationCount >= 3) {
162
+ await this._recordEvent('vulnerability_detected', {
163
+ span_id: spanId,
164
+ type: 'REASONING_LOOP',
165
+ severity: 'HIGH',
166
+ description: 'Agent reasoning entropy dropped below threshold (stagnation detected). Triggering proactive RCA.',
167
+ entropy_score: entropy
168
+ });
169
+
170
+ // Signal proactive recovery
171
+ await this.recordSelfHeal(spanId, {
172
+ type: 'PROACTIVE_RCA',
173
+ cause: 'REASONING_STAGNATION',
174
+ suggestion: 'Entropy threshold exceeded. Switch reasoning strategy.'
175
+ });
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Calculates "Reasoning Entropy" (Similarity to previous thoughts).
182
+ * Range 0.0 (High Entropy/New) to 1.0 (Low Entropy/Repetitive).
183
+ */
184
+ calculateEntropy(spanId, currentThought) {
185
+ if (!this.entropyCache.has(spanId)) {
186
+ this.entropyCache.set(spanId, []);
187
+ }
188
+ const history = this.entropyCache.get(spanId);
189
+
190
+ if (history.length === 0) {
191
+ history.push({ thought: currentThought, entropy: 0 });
192
+ return 0;
193
+ }
194
+
195
+ // Simple Jaccard Similarity approach for stagnation detection
196
+ const getTokens = (str) => new Set(str.toLowerCase().split(/\s+/).filter(t => t.length > 3));
197
+ const currentTokens = getTokens(currentThought);
198
+
199
+ let maxSimilarity = 0;
200
+ for (const prev of history) {
201
+ const prevTokens = getTokens(prev.thought);
202
+ const intersection = new Set([...currentTokens].filter(x => prevTokens.has(x)));
203
+ const union = new Set([...currentTokens, ...prevTokens]);
204
+ const similarity = union.size === 0 ? 0 : intersection.size / union.size;
205
+ if (similarity > maxSimilarity) maxSimilarity = similarity;
206
+ }
207
+
208
+ history.push({ thought: currentThought, entropy: maxSimilarity });
209
+ if (history.length > 5) history.shift(); // Sliding window of 5 thoughts
210
+
211
+ return maxSimilarity;
132
212
  }
133
213
 
134
214
  /**
135
215
  * Internal AUDIT writer.
136
216
  */
137
- _recordEvent(event, data) {
217
+ async _recordEvent(event, data) {
138
218
  const entry = {
139
219
  id: crypto.randomUUID(),
140
220
  timestamp: new Date().toISOString(),
@@ -149,7 +229,7 @@ class NexusTracer {
149
229
  entry.did = this.did;
150
230
  // Sign the stringified entry WITHOUT the signature field itself
151
231
  const payload = JSON.stringify(entry);
152
- entry.signature = ztai.signData(this.did, payload);
232
+ entry.signature = await ztai.signData(this.did, payload);
153
233
  } catch (err) {
154
234
  console.warn(`[NexusTracer] ZTAI signing failed: ${err.message}`);
155
235
  }
@@ -168,8 +248,8 @@ class NexusTracer {
168
248
  /**
169
249
  * Records a FinOps budget decision (Pillar V).
170
250
  */
171
- recordFinOps(spanId, decision) {
172
- this._recordEvent('finops_decision', {
251
+ async recordFinOps(spanId, decision) {
252
+ await this._recordEvent('finops_decision', {
173
253
  span_id: spanId,
174
254
  ...decision
175
255
  });
@@ -178,8 +258,8 @@ class NexusTracer {
178
258
  /**
179
259
  * Records a Self-Healing trigger event (Pillar VI).
180
260
  */
181
- recordSelfHeal(spanId, report) {
182
- this._recordEvent('self_heal_trigger', {
261
+ async recordSelfHeal(spanId, report) {
262
+ await this._recordEvent('self_heal_trigger', {
183
263
  span_id: spanId,
184
264
  ...report
185
265
  });
@@ -188,7 +268,7 @@ class NexusTracer {
188
268
  /**
189
269
  * Finalize and export the Agentic SBOM (Pillar IV).
190
270
  */
191
- exportSBOM(outputPath = null) {
271
+ async exportSBOM(outputPath = null) {
192
272
  const finalPath = outputPath || path.join(process.cwd(), '.planning', 'MANIFEST.sbom.json');
193
273
  const manifest = {
194
274
  ...this.sbom,
@@ -203,7 +283,7 @@ class NexusTracer {
203
283
  }
204
284
  fs.writeFileSync(finalPath, JSON.stringify(manifest, null, 2));
205
285
 
206
- this._recordEvent('sbom_exported', { path: finalPath });
286
+ await this._recordEvent('sbom_exported', { path: finalPath });
207
287
  return finalPath;
208
288
  } catch (err) {
209
289
  console.error(`[NexusTracer] Failed to export SBOM: ${err.message}`);
@@ -212,4 +292,9 @@ class NexusTracer {
212
292
  }
213
293
  }
214
294
 
215
- module.exports = NexusTracer;
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 for global audit logging.
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
- * @returns {string} - Sanitized/Isolated thought chain.
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)) return thoughtChain;
49
+ sanitizeThoughtChain(thoughtChain, enclaveId, policyResult = { passed: true }) {
50
+ if (!this.activeEnclaves.has(enclaveId)) {
51
+ return { status: 'PLAINTEXT', content: thoughtChain };
52
+ }
44
53
 
45
- // Zero-Visibility Protocol: In SRE mode, the global audit log only receives a hash
46
- // or a redacted summary. The full chain is kept in volatile enclave memory.
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
- return `[SRE-ISOLATED] Confidential reasoning executed in enclave ${enclaveId}. Verification Digest: ${digest}. Original trace is isolated from persistence.`;
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
  /**
@@ -83,6 +83,33 @@ class TemporalHindsight {
83
83
  autoApplyStatus: 'PENDING_SIGNATURE',
84
84
  };
85
85
  }
86
+
87
+ /**
88
+ * v5 Pillar III: Proactive Loop Recovery
89
+ * Generates a "Steering Vector" to break agentic Reasoning Stagnation.
90
+ */
91
+ handleProactiveRecovery(traceId, entropyScore) {
92
+ console.log(`[TemporalHindsight] Proactive Recovery triggered for trace ${traceId} (Entropy: ${entropyScore})`);
93
+
94
+ const steeringVectors = [
95
+ "CRITICAL: Stagnation detected. You have repeated similar reasoning steps 3 times. STOP your current approach and decompose the problem into smaller, independent sub-tasks.",
96
+ "STATIONARY LOOP: Your recent thoughts show high similarity. Change your technical layer—if you were editing code, try running a diagnostic command instead.",
97
+ "REASONING DEADLOCK: Entropy too low. Request human intervention or switch to the 'Architect' persona to re-evaluate the plan.",
98
+ "DIVERGENCE ALERT: Proactive reset. Clear your context window of the last 3 reasoning steps and start fresh from the last successful checkpoint."
99
+ ];
100
+
101
+ // Pick a vector based on entropy severity
102
+ const index = Math.min(Math.floor(entropyScore * steeringVectors.length), steeringVectors.length - 1);
103
+
104
+ return {
105
+ timestamp: new Date().toISOString(),
106
+ trace_id: traceId,
107
+ event: 'STEERING_VECTOR_GENERATED',
108
+ entropy: entropyScore,
109
+ instruction: steeringVectors[index],
110
+ action: 'INJECT_SYSTEM_PROMPT'
111
+ };
112
+ }
86
113
  }
87
114
 
88
115
  module.exports = TemporalHindsight;
@@ -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
- * Selects the optimal provider based on weighted latency and cost.
20
- * @param {Object} options - Task requirements (maxLatency, budgetConstraint)
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(([_, data]) => data.healthy)
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
- // Score = (Latency * 0.4) + (Cost * 0.6) — Lower is better
28
- const score = (data.latency * 0.4) + (data.costMultiplier * 1000 * 0.6);
29
- return { id, score };
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', 'opus': 'gemini-1.5-pro', 'haiku': 'gemini-1.5-flash' },
63
- 'aws': { 'sonnet': 'anthropic.claude-3-5-sonnet-v2:0', 'opus': 'anthropic.claude-3-opus-v1:0', 'haiku': 'anthropic.claude-3-haiku-v1:0' },
64
- 'azure': { 'sonnet': 'gpt-4o', 'opus': 'gpt-4-turbo', 'haiku': 'gpt-35-turbo' }
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();