@neuroverseos/nv-sim 0.1.2 → 0.1.6
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/README.md +376 -66
- package/dist/adapters/mirofish.js +461 -0
- package/dist/adapters/scienceclaw.js +750 -0
- package/dist/assets/index-CHmUN8s0.js +532 -0
- package/dist/assets/index-DWgMnB7I.css +1 -0
- package/dist/assets/mirotir-logo-DUexumBH.svg +185 -0
- package/dist/assets/reportEngine-BVdQ2_nW.js +1 -0
- package/dist/components/ConstraintsPanel.js +11 -0
- package/dist/components/StakeholderBuilder.js +32 -0
- package/dist/components/ui/badge.js +24 -0
- package/dist/components/ui/button.js +70 -0
- package/dist/components/ui/card.js +57 -0
- package/dist/components/ui/input.js +44 -0
- package/dist/components/ui/label.js +45 -0
- package/dist/components/ui/select.js +70 -0
- package/dist/engine/aiProvider.js +681 -0
- package/dist/engine/auditTrace.js +352 -0
- package/dist/engine/behavioralAnalysis.js +605 -0
- package/dist/engine/cli.js +1408 -299
- package/dist/engine/dynamicsGovernance.js +588 -0
- package/dist/engine/fullGovernedLoop.js +367 -0
- package/dist/engine/governance.js +8 -3
- package/dist/engine/governedSimulation.js +114 -17
- package/dist/engine/index.js +56 -1
- package/dist/engine/liveAdapter.js +342 -0
- package/dist/engine/liveVisualizer.js +3063 -0
- package/dist/engine/metrics/science.metrics.js +335 -0
- package/dist/engine/narrativeInjection.js +305 -0
- package/dist/engine/policyEnforcement.js +1611 -0
- package/dist/engine/policyEngine.js +799 -0
- package/dist/engine/primeRadiant.js +540 -0
- package/dist/engine/reasoningEngine.js +57 -3
- package/dist/engine/reportEngine.js +97 -0
- package/dist/engine/scenarioComparison.js +463 -0
- package/dist/engine/scenarioLibrary.js +231 -0
- package/dist/engine/swarmSimulation.js +54 -1
- package/dist/engine/worldComparison.js +358 -0
- package/dist/engine/worldStorage.js +232 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.html +23 -0
- package/dist/lib/reasoningEngine.js +290 -0
- package/dist/lib/simulationAdapter.js +686 -0
- package/dist/lib/swarmParser.js +291 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/utils.js +8 -0
- package/dist/placeholder.svg +1 -0
- package/dist/robots.txt +14 -0
- package/dist/runtime/govern.js +473 -0
- package/dist/runtime/index.js +75 -0
- package/dist/runtime/types.js +11 -0
- package/package.json +17 -12
- package/variants/.gitkeep +0 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Full Governed Simulation Loop — govern() + governDynamics()
|
|
4
|
+
*
|
|
5
|
+
* The complete simulation architecture:
|
|
6
|
+
*
|
|
7
|
+
* for each round:
|
|
8
|
+
* for each agent:
|
|
9
|
+
* action → govern(action) → commit ← Layer A (action governance)
|
|
10
|
+
* updateState()
|
|
11
|
+
* governDynamics(state, trajectory, history) ← Layer B (dynamics governance)
|
|
12
|
+
*
|
|
13
|
+
* "We don't just control what agents do.
|
|
14
|
+
* We control how their actions shape the system over time."
|
|
15
|
+
*
|
|
16
|
+
* This module combines the existing govern() with the new governDynamics()
|
|
17
|
+
* into a unified simulation runner that produces rich, traceable output.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.runFullGovernedSimulation = runFullGovernedSimulation;
|
|
21
|
+
const swarmSimulation_1 = require("./swarmSimulation");
|
|
22
|
+
const govern_1 = require("../runtime/govern");
|
|
23
|
+
const dynamicsGovernance_1 = require("./dynamicsGovernance");
|
|
24
|
+
const narrativeInjection_1 = require("./narrativeInjection");
|
|
25
|
+
// ============================================
|
|
26
|
+
// AGENT REACTION → ACTION ADAPTER
|
|
27
|
+
// ============================================
|
|
28
|
+
/**
|
|
29
|
+
* Convert a SwarmAgentReaction into an AgentAction for govern().
|
|
30
|
+
*
|
|
31
|
+
* This is the bridge between MiroFish/Swarm output and NeuroVerse governance.
|
|
32
|
+
* It infers action type, magnitude, and description from the reaction content.
|
|
33
|
+
*/
|
|
34
|
+
function reactionToAction(reaction) {
|
|
35
|
+
const desc = reaction.reaction.toLowerCase();
|
|
36
|
+
// Infer action type from reaction content
|
|
37
|
+
let type = "communication";
|
|
38
|
+
if (desc.includes("sell") || desc.includes("buy") || desc.includes("trade"))
|
|
39
|
+
type = "trade";
|
|
40
|
+
if (desc.includes("post") || desc.includes("share") || desc.includes("publish"))
|
|
41
|
+
type = "post";
|
|
42
|
+
if (desc.includes("comment") || desc.includes("reply") || desc.includes("respond"))
|
|
43
|
+
type = "comment";
|
|
44
|
+
if (desc.includes("follow") || desc.includes("join") || desc.includes("coalition"))
|
|
45
|
+
type = "coalition";
|
|
46
|
+
if (desc.includes("withdraw") || desc.includes("exit") || desc.includes("leave"))
|
|
47
|
+
type = "withdrawal";
|
|
48
|
+
if (desc.includes("intervene") || desc.includes("inject") || desc.includes("regulate"))
|
|
49
|
+
type = "intervention";
|
|
50
|
+
if (desc.includes("amplif") || desc.includes("boost") || desc.includes("promote"))
|
|
51
|
+
type = "amplification";
|
|
52
|
+
if (desc.includes("attack") || desc.includes("accuse") || desc.includes("denounce"))
|
|
53
|
+
type = "hostile";
|
|
54
|
+
// Magnitude is the absolute impact
|
|
55
|
+
const magnitude = Math.abs(reaction.impact);
|
|
56
|
+
return {
|
|
57
|
+
agentId: reaction.stakeholder_id,
|
|
58
|
+
type,
|
|
59
|
+
description: reaction.reaction,
|
|
60
|
+
magnitude: Number(magnitude.toFixed(3)),
|
|
61
|
+
context: {
|
|
62
|
+
originalImpact: reaction.impact,
|
|
63
|
+
confidence: reaction.confidence,
|
|
64
|
+
trigger: reaction.trigger,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Apply a governance verdict back to a reaction.
|
|
70
|
+
*/
|
|
71
|
+
function applyVerdictToReaction(reaction, verdict) {
|
|
72
|
+
if (verdict.status === "BLOCK") {
|
|
73
|
+
return {
|
|
74
|
+
...reaction,
|
|
75
|
+
impact: Number((reaction.impact * 0.1).toFixed(3)),
|
|
76
|
+
confidence: Number((Math.max(0.05, reaction.confidence * 0.3)).toFixed(3)),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (verdict.status === "PAUSE") {
|
|
80
|
+
return {
|
|
81
|
+
...reaction,
|
|
82
|
+
impact: Number((reaction.impact * 0.5).toFixed(3)),
|
|
83
|
+
confidence: Number((Math.max(0.1, reaction.confidence * 0.6)).toFixed(3)),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (verdict.status === "MODIFY" && verdict.action) {
|
|
87
|
+
const ratio = verdict.action.magnitude / Math.max(0.001, Math.abs(reaction.impact));
|
|
88
|
+
return {
|
|
89
|
+
...reaction,
|
|
90
|
+
impact: Number((reaction.impact * ratio).toFixed(3)),
|
|
91
|
+
confidence: Number(reaction.confidence.toFixed(3)),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// ALLOW — no change
|
|
95
|
+
return reaction;
|
|
96
|
+
}
|
|
97
|
+
// ============================================
|
|
98
|
+
// DETECT EMERGENT DYNAMICS
|
|
99
|
+
// ============================================
|
|
100
|
+
function detectEmergentDynamics(reactions, dynamicsInterventions, systemState) {
|
|
101
|
+
const dynamics = [];
|
|
102
|
+
const avgImpact = reactions.reduce((s, r) => s + r.impact, 0) / reactions.length;
|
|
103
|
+
if (Math.abs(avgImpact) > 0.5) {
|
|
104
|
+
dynamics.push(avgImpact > 0
|
|
105
|
+
? "Strong positive consensus — governance channeled energy constructively"
|
|
106
|
+
: "Negative consensus persists despite governance intervention");
|
|
107
|
+
}
|
|
108
|
+
else if (Math.abs(avgImpact) < 0.12) {
|
|
109
|
+
dynamics.push("Near-equilibrium achieved — agent positions stabilized by governance");
|
|
110
|
+
}
|
|
111
|
+
const positive = reactions.filter((r) => r.impact > 0.2).length;
|
|
112
|
+
const negative = reactions.filter((r) => r.impact < -0.2).length;
|
|
113
|
+
if (positive > 0 && negative > 0 && Math.abs(positive - negative) <= 1) {
|
|
114
|
+
dynamics.push("Polarization detected but contained by dynamics governance");
|
|
115
|
+
}
|
|
116
|
+
if (systemState.coolingPeriod > 0) {
|
|
117
|
+
dynamics.push(`Cooling period active — reactive posting suppressed for ${systemState.coolingPeriod} more round(s)`);
|
|
118
|
+
}
|
|
119
|
+
if (systemState.cascadeRisk > 0.7) {
|
|
120
|
+
dynamics.push(`CASCADE WARNING: risk at ${(systemState.cascadeRisk * 100).toFixed(0)}% — circuit breakers engaged`);
|
|
121
|
+
}
|
|
122
|
+
if (systemState.trust < 0.3) {
|
|
123
|
+
dynamics.push(`TRUST CRISIS: institutional trust at ${(systemState.trust * 100).toFixed(0)}% — visibility shift active`);
|
|
124
|
+
}
|
|
125
|
+
if (dynamicsInterventions.length > 0) {
|
|
126
|
+
dynamics.push(`${dynamicsInterventions.length} dynamics intervention(s) applied this round`);
|
|
127
|
+
}
|
|
128
|
+
return dynamics;
|
|
129
|
+
}
|
|
130
|
+
// ============================================
|
|
131
|
+
// MAIN: runFullGovernedSimulation()
|
|
132
|
+
// ============================================
|
|
133
|
+
/**
|
|
134
|
+
* Run a simulation with BOTH governance layers:
|
|
135
|
+
* Layer A: govern() — per-action, per-agent
|
|
136
|
+
* Layer B: governDynamics() — per-round, system-level
|
|
137
|
+
*
|
|
138
|
+
* This is the complete Prime Radiant simulation loop.
|
|
139
|
+
*/
|
|
140
|
+
async function runFullGovernedSimulation(config) {
|
|
141
|
+
// --- Initialize both governors ---
|
|
142
|
+
const actionGovernor = (0, govern_1.createGovernor)({
|
|
143
|
+
policyText: config.policyText,
|
|
144
|
+
sensitivity: 0.5,
|
|
145
|
+
});
|
|
146
|
+
const dynamicsGovernor = (0, dynamicsGovernance_1.createDynamicsGovernor)(config.policyText, config.initialSystemState);
|
|
147
|
+
// Build policy text from world definition invariants + explicit policy
|
|
148
|
+
const fullPolicyText = [
|
|
149
|
+
config.policyText,
|
|
150
|
+
...config.worldDef.invariants.map((inv) => inv.description),
|
|
151
|
+
].join("\n");
|
|
152
|
+
// Re-create action governor with full policy
|
|
153
|
+
const fullActionGovernor = (0, govern_1.createGovernor)({
|
|
154
|
+
policyText: fullPolicyText,
|
|
155
|
+
sensitivity: 0.5,
|
|
156
|
+
});
|
|
157
|
+
// --- Run raw simulation ---
|
|
158
|
+
const rawResult = await (0, swarmSimulation_1.runSwarmSimulation)(config.scenario, config.stakeholders, config.paths, config.swarmConfig);
|
|
159
|
+
// --- Process each round through both governance layers ---
|
|
160
|
+
const rounds = [];
|
|
161
|
+
const allDynamicsInterventions = [];
|
|
162
|
+
const allNarrativeImpacts = [];
|
|
163
|
+
const actionStats = {
|
|
164
|
+
totalEvaluations: 0,
|
|
165
|
+
allowed: 0,
|
|
166
|
+
blocked: 0,
|
|
167
|
+
modified: 0,
|
|
168
|
+
paused: 0,
|
|
169
|
+
totalReduction: 0,
|
|
170
|
+
rulesFired: 0,
|
|
171
|
+
};
|
|
172
|
+
for (const rawRound of rawResult.rounds) {
|
|
173
|
+
// === LAYER A: Action Governance ===
|
|
174
|
+
const actionVerdicts = [];
|
|
175
|
+
const actionGovernedReactions = [];
|
|
176
|
+
for (const reaction of rawRound.reactions) {
|
|
177
|
+
// Convert reaction to action
|
|
178
|
+
const action = reactionToAction(reaction);
|
|
179
|
+
// Build world state from current dynamics state
|
|
180
|
+
const currentState = dynamicsGovernor.state;
|
|
181
|
+
const worldState = {
|
|
182
|
+
volatility: currentState.outrage * 100,
|
|
183
|
+
liquidity: currentState.trust * 100,
|
|
184
|
+
polarization: currentState.polarization * 100,
|
|
185
|
+
cascade_risk: currentState.cascadeRisk * 100,
|
|
186
|
+
};
|
|
187
|
+
// Evaluate through action governor
|
|
188
|
+
const verdict = fullActionGovernor.evaluate(action, worldState);
|
|
189
|
+
actionStats.totalEvaluations++;
|
|
190
|
+
switch (verdict.status) {
|
|
191
|
+
case "ALLOW":
|
|
192
|
+
actionStats.allowed++;
|
|
193
|
+
break;
|
|
194
|
+
case "BLOCK":
|
|
195
|
+
actionStats.blocked++;
|
|
196
|
+
break;
|
|
197
|
+
case "MODIFY":
|
|
198
|
+
actionStats.modified++;
|
|
199
|
+
break;
|
|
200
|
+
case "PAUSE":
|
|
201
|
+
actionStats.paused++;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
actionStats.rulesFired += verdict.rulesFired.filter((r) => r.effect !== "monitored").length;
|
|
205
|
+
if (verdict.action) {
|
|
206
|
+
const reduction = 1 - (verdict.action.magnitude / Math.max(0.001, action.magnitude));
|
|
207
|
+
actionStats.totalReduction += reduction;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
actionStats.totalReduction += 1;
|
|
211
|
+
}
|
|
212
|
+
// Apply verdict to reaction
|
|
213
|
+
const governedReaction = applyVerdictToReaction(reaction, verdict);
|
|
214
|
+
actionGovernedReactions.push(governedReaction);
|
|
215
|
+
actionVerdicts.push({
|
|
216
|
+
agentId: reaction.stakeholder_id,
|
|
217
|
+
status: verdict.status,
|
|
218
|
+
originalMagnitude: action.magnitude,
|
|
219
|
+
governedMagnitude: verdict.action?.magnitude ?? null,
|
|
220
|
+
rulesFired: verdict.rulesFired.length,
|
|
221
|
+
reason: verdict.reason,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
// === NARRATIVE INJECTION (between Layer A and Layer B) ===
|
|
225
|
+
if (config.narrativeEvents && config.narrativeEvents.length > 0) {
|
|
226
|
+
const roundEvents = (0, narrativeInjection_1.getEventsForRound)(config.narrativeEvents, rawRound.round);
|
|
227
|
+
for (const event of roundEvents) {
|
|
228
|
+
const impact = (0, narrativeInjection_1.injectNarrative)(actionGovernedReactions, event, config.stakeholders);
|
|
229
|
+
allNarrativeImpacts.push(impact);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// === LAYER B: Dynamics Governance ===
|
|
233
|
+
const dynamicsResult = dynamicsGovernor.governRound(actionGovernedReactions, rawRound.round, config.agentTypes);
|
|
234
|
+
allDynamicsInterventions.push(...dynamicsResult.interventions);
|
|
235
|
+
// Detect emergent dynamics
|
|
236
|
+
const emergentDynamics = detectEmergentDynamics(dynamicsResult.reactions, dynamicsResult.interventions, dynamicsResult.systemState);
|
|
237
|
+
rounds.push({
|
|
238
|
+
round: rawRound.round,
|
|
239
|
+
rawReactions: rawRound.reactions,
|
|
240
|
+
actionGovernedReactions,
|
|
241
|
+
finalReactions: dynamicsResult.reactions,
|
|
242
|
+
actionVerdicts,
|
|
243
|
+
dynamicsInterventions: dynamicsResult.interventions,
|
|
244
|
+
dynamicsTrajectory: dynamicsResult.trajectory,
|
|
245
|
+
systemState: dynamicsResult.systemState,
|
|
246
|
+
emergentDynamics,
|
|
247
|
+
predictedRisk: dynamicsResult.predictedRisk,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
// --- Compute aggregate metrics ---
|
|
251
|
+
const finalState = dynamicsGovernor.state;
|
|
252
|
+
const stateTimeline = finalState.history;
|
|
253
|
+
// Overall trajectory
|
|
254
|
+
const trajectory = computeOverallTrajectory(rounds);
|
|
255
|
+
// System health score (0-100)
|
|
256
|
+
const systemHealthScore = computeSystemHealth(finalState, actionStats, dynamicsGovernor.stats);
|
|
257
|
+
// Inflection points
|
|
258
|
+
const inflectionPoints = findInflectionPoints(rounds);
|
|
259
|
+
const avgReduction = actionStats.totalEvaluations > 0
|
|
260
|
+
? actionStats.totalReduction / actionStats.totalEvaluations
|
|
261
|
+
: 0;
|
|
262
|
+
return {
|
|
263
|
+
scenario: config.scenario,
|
|
264
|
+
policyText: config.policyText,
|
|
265
|
+
worldRules: {
|
|
266
|
+
thesis: config.worldDef.thesis,
|
|
267
|
+
invariantCount: config.worldDef.invariants.length,
|
|
268
|
+
gateCount: config.worldDef.gates?.length ?? 0,
|
|
269
|
+
},
|
|
270
|
+
rounds,
|
|
271
|
+
stateTimeline,
|
|
272
|
+
actionGovernance: {
|
|
273
|
+
totalEvaluations: actionStats.totalEvaluations,
|
|
274
|
+
allowed: actionStats.allowed,
|
|
275
|
+
blocked: actionStats.blocked,
|
|
276
|
+
modified: actionStats.modified,
|
|
277
|
+
paused: actionStats.paused,
|
|
278
|
+
avgReduction: Number(avgReduction.toFixed(3)),
|
|
279
|
+
rulesFired: actionStats.rulesFired,
|
|
280
|
+
},
|
|
281
|
+
dynamicsGovernance: dynamicsGovernor.stats,
|
|
282
|
+
allDynamicsInterventions,
|
|
283
|
+
narrativeImpacts: allNarrativeImpacts,
|
|
284
|
+
trajectory,
|
|
285
|
+
systemHealthScore,
|
|
286
|
+
inflectionPoints,
|
|
287
|
+
finalState,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
// ============================================
|
|
291
|
+
// HELPERS
|
|
292
|
+
// ============================================
|
|
293
|
+
function computeOverallTrajectory(rounds) {
|
|
294
|
+
if (rounds.length < 2)
|
|
295
|
+
return "stabilizing";
|
|
296
|
+
const avgImpacts = rounds.map((r) => r.finalReactions.reduce((s, rx) => s + rx.impact, 0) / r.finalReactions.length);
|
|
297
|
+
const trend = avgImpacts[avgImpacts.length - 1] - avgImpacts[0];
|
|
298
|
+
const mean = avgImpacts.reduce((s, x) => s + x, 0) / avgImpacts.length;
|
|
299
|
+
const variance = avgImpacts.reduce((s, v) => s + (v - mean) ** 2, 0) / avgImpacts.length;
|
|
300
|
+
if (variance > 0.15)
|
|
301
|
+
return "diverging";
|
|
302
|
+
if (Math.abs(trend) < 0.1 && variance < 0.05)
|
|
303
|
+
return "converging";
|
|
304
|
+
if (trend < -0.2)
|
|
305
|
+
return "escalating";
|
|
306
|
+
return "stabilizing";
|
|
307
|
+
}
|
|
308
|
+
function computeSystemHealth(finalState, actionStats, dynamicsStats) {
|
|
309
|
+
let score = 50; // baseline
|
|
310
|
+
// Trust contribution (+/- 20)
|
|
311
|
+
score += (finalState.trust - 0.5) * 40;
|
|
312
|
+
// Low outrage is good (+/- 15)
|
|
313
|
+
score += (0.5 - finalState.outrage) * 30;
|
|
314
|
+
// Low polarization is good (+/- 10)
|
|
315
|
+
score += (0.5 - finalState.polarization) * 20;
|
|
316
|
+
// Low cascade risk is good (+/- 10)
|
|
317
|
+
score += (0.5 - finalState.cascadeRisk) * 20;
|
|
318
|
+
// Governance activity is neutral-positive (shows the system is working)
|
|
319
|
+
if (dynamicsStats.totalInterventions > 0) {
|
|
320
|
+
score += 5; // System was active
|
|
321
|
+
}
|
|
322
|
+
// Over-blocking is bad
|
|
323
|
+
const blockRate = actionStats.totalEvaluations > 0
|
|
324
|
+
? actionStats.blocked / actionStats.totalEvaluations
|
|
325
|
+
: 0;
|
|
326
|
+
if (blockRate > 0.7)
|
|
327
|
+
score -= 15; // Over-constrained
|
|
328
|
+
return Math.max(0, Math.min(100, Math.round(score)));
|
|
329
|
+
}
|
|
330
|
+
function findInflectionPoints(rounds) {
|
|
331
|
+
const points = [];
|
|
332
|
+
for (let i = 1; i < rounds.length; i++) {
|
|
333
|
+
const prev = rounds[i - 1];
|
|
334
|
+
const curr = rounds[i];
|
|
335
|
+
// System state transitions
|
|
336
|
+
if (prev.dynamicsTrajectory !== curr.dynamicsTrajectory) {
|
|
337
|
+
points.push(`Round ${i}: Trajectory shifted ${prev.dynamicsTrajectory} → ${curr.dynamicsTrajectory}`);
|
|
338
|
+
}
|
|
339
|
+
// Cascade breaker activation
|
|
340
|
+
const cascadeBreaker = curr.dynamicsInterventions.find((d) => d.type === "CASCADE_BREAKER");
|
|
341
|
+
if (cascadeBreaker) {
|
|
342
|
+
points.push(`Round ${i}: CASCADE CIRCUIT BREAKER activated — ${cascadeBreaker.description}`);
|
|
343
|
+
}
|
|
344
|
+
// Cooling period start
|
|
345
|
+
if (prev.systemState.coolingPeriod === 0 && curr.systemState.coolingPeriod > 0) {
|
|
346
|
+
points.push(`Round ${i}: Cooling period triggered — system entering dampened state`);
|
|
347
|
+
}
|
|
348
|
+
// Trust crisis
|
|
349
|
+
if (prev.systemState.trust >= 0.3 && curr.systemState.trust < 0.3) {
|
|
350
|
+
points.push(`Round ${i}: TRUST CRISIS — institutional trust dropped below 30%`);
|
|
351
|
+
}
|
|
352
|
+
// Trust recovery
|
|
353
|
+
if (prev.systemState.trust < 0.3 && curr.systemState.trust >= 0.3) {
|
|
354
|
+
points.push(`Round ${i}: Trust recovery — institutional trust climbed back above 30%`);
|
|
355
|
+
}
|
|
356
|
+
// Major sentiment shift
|
|
357
|
+
const prevAvg = prev.finalReactions.reduce((s, r) => s + r.impact, 0) / prev.finalReactions.length;
|
|
358
|
+
const currAvg = curr.finalReactions.reduce((s, r) => s + r.impact, 0) / curr.finalReactions.length;
|
|
359
|
+
if (Math.abs(currAvg - prevAvg) > 0.3) {
|
|
360
|
+
points.push(`Round ${i}: Major sentiment shift (${(Math.abs(currAvg - prevAvg) * 100).toFixed(0)}% swing)`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (points.length === 0) {
|
|
364
|
+
points.push("Governance prevented major inflection points — smooth trajectory maintained");
|
|
365
|
+
}
|
|
366
|
+
return points;
|
|
367
|
+
}
|
|
@@ -110,10 +110,15 @@ function runFullGovernanceChecks(request, worldLite) {
|
|
|
110
110
|
// Convert verdict to constitutional checks
|
|
111
111
|
const nvChecks = (0, worldBridge_1.verdictToConstitutionalChecks)(guardVerdict);
|
|
112
112
|
results.push(...nvChecks);
|
|
113
|
-
// If guard blocks,
|
|
113
|
+
// If guard blocks, note the intervention but continue in advisory mode
|
|
114
|
+
// (Reasoning continues so user can see what governance changed)
|
|
114
115
|
if (guardVerdict.status === "BLOCK") {
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
results.push({
|
|
117
|
+
rule_id: "NV-BLOCK",
|
|
118
|
+
rule: "NeuroverseOS guard issued BLOCK — governance intervention active",
|
|
119
|
+
passed: false,
|
|
120
|
+
detail: `Guard blocked: ${guardVerdict.reason ?? "Action blocked by guard"}. Continuing in governed advisory mode.`,
|
|
121
|
+
});
|
|
117
122
|
}
|
|
118
123
|
// If guard pauses, note it but continue (reasoning is advisory)
|
|
119
124
|
if (guardVerdict.status === "PAUSE") {
|
|
@@ -17,6 +17,8 @@ exports.TRADING_DEMO = void 0;
|
|
|
17
17
|
exports.runGovernedComparison = runGovernedComparison;
|
|
18
18
|
const swarmSimulation_1 = require("./swarmSimulation");
|
|
19
19
|
const worldBridge_1 = require("./worldBridge");
|
|
20
|
+
const narrativeInjection_1 = require("./narrativeInjection");
|
|
21
|
+
const behavioralAnalysis_1 = require("./behavioralAnalysis");
|
|
20
22
|
// ============================================
|
|
21
23
|
// GOVERNED REACTION MODEL
|
|
22
24
|
// ============================================
|
|
@@ -30,20 +32,79 @@ const worldBridge_1 = require("./worldBridge");
|
|
|
30
32
|
*/
|
|
31
33
|
function applyGovernanceToReactions(reactions, invariants, gates, roundIndex, cumulativeAvgImpact) {
|
|
32
34
|
const interventions = [];
|
|
35
|
+
const enforcementLog = [];
|
|
33
36
|
const governed = reactions.map((r) => ({ ...r }));
|
|
34
37
|
// --- Invariant enforcement ---
|
|
35
|
-
|
|
36
|
-
const enforceableInvariants = invariants.filter((inv) => inv.enforceable);
|
|
37
|
-
for (const inv of enforceableInvariants) {
|
|
38
|
+
for (const inv of invariants) {
|
|
38
39
|
const desc = inv.description.toLowerCase();
|
|
40
|
+
let fired = false;
|
|
41
|
+
if (!inv.enforceable) {
|
|
42
|
+
// Advisory invariants: log but don't reshape
|
|
43
|
+
enforcementLog.push({ id: inv.id, description: inv.description, level: "advisory", fired: false });
|
|
44
|
+
if (roundIndex === 0) {
|
|
45
|
+
interventions.push(`[ADVISORY] ${inv.description} — monitored, not enforced`);
|
|
46
|
+
}
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// === Block/prohibit patterns (user language: "Block panic selling") ===
|
|
50
|
+
// Hard block: cap matching actions to near-zero impact
|
|
51
|
+
if (desc.includes("block") || desc.includes("prohibit") || desc.includes("prevent") || desc.includes("forbid") || desc.includes("ban") || desc.includes("stop")) {
|
|
52
|
+
for (const agent of governed) {
|
|
53
|
+
// Match the blocked action pattern against agent reactions
|
|
54
|
+
const reactionLower = agent.reaction?.toLowerCase() ?? "";
|
|
55
|
+
const actionTerms = desc.replace(/\b(block|prohibit|prevent|forbid|ban|stop)\b/g, "").trim().split(/\s+/).filter(t => t.length > 2);
|
|
56
|
+
const matches = actionTerms.some(term => reactionLower.includes(term)) || agent.impact < -0.3;
|
|
57
|
+
if (matches) {
|
|
58
|
+
const original = agent.impact;
|
|
59
|
+
agent.impact = agent.impact * 0.15; // Hard suppression
|
|
60
|
+
agent.confidence = Math.max(0.1, agent.confidence * 0.4);
|
|
61
|
+
if (Math.abs(original - agent.impact) > 0.01) {
|
|
62
|
+
fired = true;
|
|
63
|
+
interventions.push(`[BLOCK] ${agent.stakeholder_id}: "${inv.description}" — impact crushed ${original.toFixed(2)} → ${agent.impact.toFixed(2)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// === Panic / cascade / contagion patterns ===
|
|
69
|
+
// Circuit breaker: dampen all agents when panic conditions detected
|
|
70
|
+
if (desc.includes("panic") || desc.includes("cascade") || desc.includes("contagion") || desc.includes("runaway") || desc.includes("crash")) {
|
|
71
|
+
const negativeCount = governed.filter(a => a.impact < -0.2).length;
|
|
72
|
+
const panicThreshold = Math.max(1, Math.floor(governed.length * 0.4));
|
|
73
|
+
if (negativeCount >= panicThreshold || cumulativeAvgImpact < -0.2) {
|
|
74
|
+
const dampingFactor = 0.3;
|
|
75
|
+
for (const agent of governed) {
|
|
76
|
+
if (agent.impact < -0.15) {
|
|
77
|
+
const original = agent.impact;
|
|
78
|
+
agent.impact = agent.impact * dampingFactor;
|
|
79
|
+
fired = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
interventions.push(`[BLOCK] PANIC CIRCUIT BREAKER: "${inv.description}" — ${negativeCount} agents in panic, negative impacts crushed by ${((1 - dampingFactor) * 100).toFixed(0)}%`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// === Slow / cool / gradual patterns ===
|
|
86
|
+
// Dampening: reduce all extreme movements
|
|
87
|
+
if (desc.includes("slow") || desc.includes("cool") || desc.includes("gradual") || desc.includes("dampen") || desc.includes("reduce volatility")) {
|
|
88
|
+
for (const agent of governed) {
|
|
89
|
+
if (Math.abs(agent.impact) > 0.3) {
|
|
90
|
+
const original = agent.impact;
|
|
91
|
+
agent.impact = agent.impact * 0.65;
|
|
92
|
+
if (Math.abs(original - agent.impact) > 0.01) {
|
|
93
|
+
fired = true;
|
|
94
|
+
interventions.push(`[PAUSE] ${agent.stakeholder_id}: "${inv.description}" — dampened ${original.toFixed(2)} → ${agent.impact.toFixed(2)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
39
99
|
// Cap extreme negative impacts (e.g., "maintain liquidity floor")
|
|
40
100
|
if (desc.includes("floor") || desc.includes("maintain") || desc.includes("minimum")) {
|
|
41
101
|
for (const agent of governed) {
|
|
42
|
-
if (agent.impact < -0.
|
|
102
|
+
if (agent.impact < -0.35) {
|
|
43
103
|
const original = agent.impact;
|
|
44
|
-
agent.impact = Math.max(agent.impact, -0.
|
|
104
|
+
agent.impact = Math.max(agent.impact, -0.3);
|
|
45
105
|
if (original !== agent.impact) {
|
|
46
|
-
|
|
106
|
+
fired = true;
|
|
107
|
+
interventions.push(`[BLOCK] ${agent.stakeholder_id}: Liquidity floor violated — impact capped ${original.toFixed(2)} → ${agent.impact.toFixed(2)} (${inv.description})`);
|
|
47
108
|
}
|
|
48
109
|
}
|
|
49
110
|
}
|
|
@@ -51,11 +112,12 @@ function applyGovernanceToReactions(reactions, invariants, gates, roundIndex, cu
|
|
|
51
112
|
// Limit excessive positive speculation (e.g., "no leverage > 5x")
|
|
52
113
|
if (desc.includes("leverage") || desc.includes("limit") || desc.includes("cap") || desc.includes("restrict")) {
|
|
53
114
|
for (const agent of governed) {
|
|
54
|
-
if (agent.impact > 0.
|
|
115
|
+
if (agent.impact > 0.45) {
|
|
55
116
|
const original = agent.impact;
|
|
56
|
-
agent.impact = Math.min(agent.impact, 0.
|
|
117
|
+
agent.impact = Math.min(agent.impact, 0.4);
|
|
57
118
|
if (original !== agent.impact) {
|
|
58
|
-
|
|
119
|
+
fired = true;
|
|
120
|
+
interventions.push(`[BLOCK] ${agent.stakeholder_id}: Leverage limit exceeded — position capped ${original.toFixed(2)} → ${agent.impact.toFixed(2)} (${inv.description})`);
|
|
59
121
|
}
|
|
60
122
|
}
|
|
61
123
|
}
|
|
@@ -65,10 +127,11 @@ function applyGovernanceToReactions(reactions, invariants, gates, roundIndex, cu
|
|
|
65
127
|
// Pull extreme reactions toward the mean
|
|
66
128
|
const avgImpact = governed.reduce((s, a) => s + a.impact, 0) / governed.length;
|
|
67
129
|
for (const agent of governed) {
|
|
68
|
-
if (Math.abs(agent.impact - avgImpact) > 0.
|
|
130
|
+
if (Math.abs(agent.impact - avgImpact) > 0.25) {
|
|
69
131
|
const original = agent.impact;
|
|
70
132
|
agent.impact = agent.impact * 0.7 + avgImpact * 0.3;
|
|
71
|
-
|
|
133
|
+
fired = true;
|
|
134
|
+
interventions.push(`[PAUSE] Rebalanced ${agent.stakeholder_id}: drift ${original.toFixed(2)} → ${agent.impact.toFixed(2)} (${inv.description})`);
|
|
72
135
|
}
|
|
73
136
|
}
|
|
74
137
|
}
|
|
@@ -79,11 +142,13 @@ function applyGovernanceToReactions(reactions, invariants, gates, roundIndex, cu
|
|
|
79
142
|
const original = agent.impact;
|
|
80
143
|
agent.impact = agent.impact * 0.8; // 20% reduction in hostility
|
|
81
144
|
if (Math.abs(original - agent.impact) > 0.01) {
|
|
145
|
+
fired = true;
|
|
82
146
|
interventions.push(`[${inv.id}] Trust gate dampened ${agent.stakeholder_id} hostility: ${original.toFixed(2)} → ${agent.impact.toFixed(2)}`);
|
|
83
147
|
}
|
|
84
148
|
}
|
|
85
149
|
}
|
|
86
150
|
}
|
|
151
|
+
enforcementLog.push({ id: inv.id, description: inv.description, level: "full", fired });
|
|
87
152
|
}
|
|
88
153
|
// --- Gate enforcement ---
|
|
89
154
|
// Gates trigger systemic interventions when thresholds are crossed
|
|
@@ -92,7 +157,7 @@ function applyGovernanceToReactions(reactions, invariants, gates, roundIndex, cu
|
|
|
92
157
|
if (gate.severity === "critical") {
|
|
93
158
|
// Critical gates act as circuit breakers — compress all impacts toward zero
|
|
94
159
|
const avgAbsImpact = governed.reduce((s, a) => s + Math.abs(a.impact), 0) / governed.length;
|
|
95
|
-
if (avgAbsImpact > 0.
|
|
160
|
+
if (avgAbsImpact > 0.3 || cumulativeAvgImpact < -0.15) {
|
|
96
161
|
const dampingFactor = 0.6;
|
|
97
162
|
for (const agent of governed) {
|
|
98
163
|
const original = agent.impact;
|
|
@@ -120,7 +185,7 @@ function applyGovernanceToReactions(reactions, invariants, gates, roundIndex, cu
|
|
|
120
185
|
agent.impact = Number(agent.impact.toFixed(2));
|
|
121
186
|
agent.confidence = Number(agent.confidence.toFixed(2));
|
|
122
187
|
}
|
|
123
|
-
return { reactions: governed, interventions };
|
|
188
|
+
return { reactions: governed, interventions, enforcementLog };
|
|
124
189
|
}
|
|
125
190
|
// ============================================
|
|
126
191
|
// GOVERNED SWARM SIMULATION
|
|
@@ -131,13 +196,15 @@ function applyGovernanceToReactions(reactions, invariants, gates, roundIndex, cu
|
|
|
131
196
|
* Same agents, same scenario, same reaction model —
|
|
132
197
|
* but world rules reshape the outcomes.
|
|
133
198
|
*/
|
|
134
|
-
async function runGovernedSwarmSimulation(scenario, stakeholders, paths, config, worldDef, nvWorld, request) {
|
|
199
|
+
async function runGovernedSwarmSimulation(scenario, stakeholders, paths, config, worldDef, nvWorld, request, narrativeEvents) {
|
|
135
200
|
// First run the unmodified simulation to get raw reactions
|
|
136
201
|
const rawResult = await (0, swarmSimulation_1.runSwarmSimulation)(scenario, stakeholders, paths, config);
|
|
137
202
|
// Now re-run applying governance to each round
|
|
138
203
|
const governedRounds = [];
|
|
139
204
|
const allInterventions = [];
|
|
140
205
|
const allGuardVerdicts = [];
|
|
206
|
+
const allNarrativeImpacts = [];
|
|
207
|
+
let latestEnforcementLog = [];
|
|
141
208
|
let cumulativeAvgImpact = 0;
|
|
142
209
|
for (const round of rawResult.rounds) {
|
|
143
210
|
// --- Real governance engine: evaluate each agent's action ---
|
|
@@ -159,8 +226,17 @@ async function runGovernedSwarmSimulation(scenario, stakeholders, paths, config,
|
|
|
159
226
|
}
|
|
160
227
|
}
|
|
161
228
|
}
|
|
229
|
+
// --- Narrative injection: information shocks ---
|
|
230
|
+
if (narrativeEvents && narrativeEvents.length > 0) {
|
|
231
|
+
const roundEvents = (0, narrativeInjection_1.getEventsForRound)(narrativeEvents, round.round);
|
|
232
|
+
for (const event of roundEvents) {
|
|
233
|
+
const impact = (0, narrativeInjection_1.injectNarrative)(round.reactions, event, stakeholders);
|
|
234
|
+
allNarrativeImpacts.push(impact);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
162
237
|
// --- Heuristic governance: invariant/gate enforcement (always applied) ---
|
|
163
|
-
const { reactions: governedReactions, interventions } = applyGovernanceToReactions(round.reactions, worldDef.invariants, worldDef.gates ?? [], round.round, cumulativeAvgImpact);
|
|
238
|
+
const { reactions: governedReactions, interventions, enforcementLog } = applyGovernanceToReactions(round.reactions, worldDef.invariants, worldDef.gates ?? [], round.round, cumulativeAvgImpact);
|
|
239
|
+
latestEnforcementLog = [...enforcementLog];
|
|
164
240
|
// Recompute emergent dynamics with governed reactions
|
|
165
241
|
const emergentDynamics = detectGovernedDynamics(governedReactions, interventions);
|
|
166
242
|
governedRounds.push({
|
|
@@ -184,6 +260,8 @@ async function runGovernedSwarmSimulation(scenario, stakeholders, paths, config,
|
|
|
184
260
|
},
|
|
185
261
|
allInterventions,
|
|
186
262
|
guardVerdicts: allGuardVerdicts,
|
|
263
|
+
narrativeImpacts: allNarrativeImpacts,
|
|
264
|
+
enforcementLog: latestEnforcementLog,
|
|
187
265
|
};
|
|
188
266
|
}
|
|
189
267
|
function detectGovernedDynamics(reactions, interventions) {
|
|
@@ -323,7 +401,7 @@ function buildComparisonNarrative(baseline, governed, worldThesis) {
|
|
|
323
401
|
* 3. Compare metrics
|
|
324
402
|
* 4. Generate narrative
|
|
325
403
|
*/
|
|
326
|
-
async function runGovernedComparison(request, worldLite, paths) {
|
|
404
|
+
async function runGovernedComparison(request, worldLite, paths, narrativeEvents) {
|
|
327
405
|
const stakeholders = (request.stakeholders ?? []).map((s) => typeof s === "string" ? { id: s, disposition: "unknown" } : s);
|
|
328
406
|
const swarmConfig = request.swarm ?? {
|
|
329
407
|
enabled: true,
|
|
@@ -336,7 +414,7 @@ async function runGovernedComparison(request, worldLite, paths) {
|
|
|
336
414
|
const baselineSwarm = await (0, swarmSimulation_1.runSwarmSimulation)(request.scenario, stakeholders, paths, swarmConfig);
|
|
337
415
|
const baselineMetrics = computeMetrics(baselineSwarm);
|
|
338
416
|
// Step 2: Governed — same scenario, world rules applied (real engine + heuristics)
|
|
339
|
-
const { swarm: governedSwarm, guardVerdicts } = await runGovernedSwarmSimulation(request.scenario, stakeholders, paths, swarmConfig, worldLite, nvWorld, request);
|
|
417
|
+
const { swarm: governedSwarm, guardVerdicts, narrativeImpacts: collectedNarrativeImpacts, enforcementLog: enforcementClassification } = await runGovernedSwarmSimulation(request.scenario, stakeholders, paths, swarmConfig, worldLite, nvWorld, request, narrativeEvents);
|
|
340
418
|
const governedMetrics = computeMetrics(governedSwarm);
|
|
341
419
|
// Step 3: Build world simulation (deterministic state evolution via real engine)
|
|
342
420
|
let worldSimulation;
|
|
@@ -392,6 +470,8 @@ async function runGovernedComparison(request, worldLite, paths) {
|
|
|
392
470
|
invariantsChecked,
|
|
393
471
|
triggeredGuards: [...triggeredGuardIds],
|
|
394
472
|
};
|
|
473
|
+
// Step 6: Behavioral analysis — what agents did differently
|
|
474
|
+
const behavioralAnalysis = (0, behavioralAnalysis_1.analyzeBehavior)(baselineSwarm.rounds, governedSwarm.rounds);
|
|
395
475
|
return {
|
|
396
476
|
scenario: request.scenario,
|
|
397
477
|
worldRules: {
|
|
@@ -417,6 +497,9 @@ async function runGovernedComparison(request, worldLite, paths) {
|
|
|
417
497
|
narrative,
|
|
418
498
|
},
|
|
419
499
|
governanceStats,
|
|
500
|
+
enforcementClassification: enforcementClassification.length > 0 ? enforcementClassification : undefined,
|
|
501
|
+
narrativeImpacts: collectedNarrativeImpacts.length > 0 ? collectedNarrativeImpacts : undefined,
|
|
502
|
+
behavioralAnalysis,
|
|
420
503
|
};
|
|
421
504
|
}
|
|
422
505
|
// ============================================
|
|
@@ -478,6 +561,20 @@ exports.TRADING_DEMO = {
|
|
|
478
561
|
{ id: "GATE-002", label: "Contagion Warning", condition: "contagion_spread == systemic", severity: "critical" },
|
|
479
562
|
{ id: "GATE-003", label: "Leverage Warning", condition: "leverage_ratio > 8", severity: "warning" },
|
|
480
563
|
],
|
|
564
|
+
ai_roles: [
|
|
565
|
+
{
|
|
566
|
+
id: "ai_translator",
|
|
567
|
+
type: "ai",
|
|
568
|
+
permissions: ["translate_input"],
|
|
569
|
+
constraints: ["must_output_valid_schema", "no_invention_of_events", "confidence_must_be_provided"],
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
id: "ai_analyst",
|
|
573
|
+
type: "ai",
|
|
574
|
+
permissions: ["generate_report", "summarize_trace"],
|
|
575
|
+
constraints: ["must_reference_trace", "must_include_blocked_actions", "must_include_metrics", "no_unverifiable_claims"],
|
|
576
|
+
},
|
|
577
|
+
],
|
|
481
578
|
},
|
|
482
579
|
paths: [
|
|
483
580
|
{
|