@neuroverseos/nv-sim 0.1.4 → 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 +94 -0
- 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/{reportEngine-BfteK4MN.js → reportEngine-BVdQ2_nW.js} +1 -1
- 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 +427 -2
- package/dist/engine/auditTrace.js +352 -0
- package/dist/engine/behavioralAnalysis.js +605 -0
- package/dist/engine/cli.js +1087 -13
- package/dist/engine/dynamicsGovernance.js +588 -0
- package/dist/engine/fullGovernedLoop.js +367 -0
- package/dist/engine/governedSimulation.js +77 -6
- package/dist/engine/index.js +41 -1
- package/dist/engine/liveVisualizer.js +1381 -175
- package/dist/engine/metrics/science.metrics.js +335 -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/scenarioComparison.js +463 -0
- package/dist/engine/swarmSimulation.js +54 -1
- package/dist/engine/worldComparison.js +164 -0
- package/dist/engine/worldStorage.js +232 -0
- package/dist/index.html +2 -2
- 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/runtime/govern.js +473 -0
- package/dist/runtime/index.js +75 -0
- package/dist/runtime/types.js +11 -0
- package/package.json +5 -2
- package/dist/assets/index-DHKd4rcV.js +0 -338
- package/dist/assets/index-SyyA3z3U.css +0 -1
- package/dist/assets/swarmSimulation-DHDqjfMa.js +0 -1
package/README.md
CHANGED
|
@@ -291,10 +291,104 @@ npx @neuroverseos/nv-sim chaos --runs 500
|
|
|
291
291
|
npx @neuroverseos/nv-sim serve
|
|
292
292
|
```
|
|
293
293
|
|
|
294
|
+
## Policy Enforcement — The Product Loop
|
|
295
|
+
|
|
296
|
+
Write rules in plain English. Run the same scenario. See what changes. Adjust and repeat.
|
|
297
|
+
|
|
298
|
+
### Step 1: See it work (zero config)
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
npx nv-sim enforce
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Runs three iterations automatically: no rules → light rules → full rules. You see divergence immediately.
|
|
305
|
+
|
|
306
|
+
### Step 2: Write your own rules
|
|
307
|
+
|
|
308
|
+
Create a text file. That's it.
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
# my-rules.txt
|
|
312
|
+
|
|
313
|
+
Block panic selling during high volatility
|
|
314
|
+
Limit leverage to 5x
|
|
315
|
+
Maintain minimum liquidity floor
|
|
316
|
+
Slow down algorithmic trading when contagion spreads
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Step 3: Run it
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
npx nv-sim enforce trading my-rules.txt
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
The engine parses your plain English into governance rules, runs the scenario, and shows what changed.
|
|
326
|
+
|
|
327
|
+
### Step 4: Change a rule. Run again.
|
|
328
|
+
|
|
329
|
+
Remove "Limit leverage to 5x". Run again. Did stability drop? That rule was load-bearing.
|
|
330
|
+
|
|
331
|
+
Add "Require transparency for all large trades". Run again. Did effectiveness improve?
|
|
332
|
+
|
|
333
|
+
The report tracks every change:
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
RULE CHANGES
|
|
337
|
+
Run 2:
|
|
338
|
+
+ Block panic selling during high volatility
|
|
339
|
+
+ Slow down algorithmic trading when contagion spreads
|
|
340
|
+
- Limit leverage to 5x
|
|
341
|
+
|
|
342
|
+
DIVERGENCE ANALYSIS
|
|
343
|
+
Stability trend: 79% → 98%
|
|
344
|
+
Effectiveness trend: 11% → 32%
|
|
345
|
+
|
|
346
|
+
KEY INSIGHT
|
|
347
|
+
Enforcement gates are the key differentiator. Rules alone: 11%. Rules + gates: 32%.
|
|
348
|
+
|
|
349
|
+
TRY THIS EXPERIMENT
|
|
350
|
+
Remove this rule and see what happens:
|
|
351
|
+
Remove "Block panic selling during high volatility" from your rules file, then run again.
|
|
352
|
+
If stability drops, that rule was load-bearing. If nothing changes, it was noise.
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Step 5: Compare two rule sets side by side
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
npx nv-sim enforce trading light-rules.txt strict-rules.txt
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Rule patterns
|
|
362
|
+
|
|
363
|
+
The engine understands these patterns in plain English:
|
|
364
|
+
|
|
365
|
+
| Pattern | What it does | Example |
|
|
366
|
+
|---------|-------------|---------|
|
|
367
|
+
| `Block X` | Hard suppression of matching actions | `Block panic selling` |
|
|
368
|
+
| `Limit X` / `Cap X` | Caps extreme positions | `Limit leverage to 5x` |
|
|
369
|
+
| `Slow X` / `Dampen X` | Reduces large movements | `Slow down algorithmic trading` |
|
|
370
|
+
| `Maintain X` / `Floor X` | Enforces minimum thresholds | `Maintain minimum liquidity` |
|
|
371
|
+
| `Rebalance X` | Pulls extremes toward equilibrium | `Rebalance correlated positions` |
|
|
372
|
+
| `Require X` | Enforceable structural constraint | `Require transparency for large trades` |
|
|
373
|
+
| `Monitor X` | Generates a circuit breaker gate | `Monitor contagion spread` |
|
|
374
|
+
|
|
375
|
+
### Other scenarios
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
npx nv-sim enforce strait_of_hormuz my-rules.txt # Same rules, different scenario
|
|
379
|
+
npx nv-sim enforce ai_regulation_crisis # Default progressive run
|
|
380
|
+
npx nv-sim enforce trading --output=report.json # Save as JSON
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Advanced: JSON world files
|
|
384
|
+
|
|
385
|
+
For full control over gates, state variables, and thesis, use JSON world files. See `examples/worlds/` for templates. Enforce accepts both `.txt` and `.json` — mix and match.
|
|
386
|
+
|
|
294
387
|
## Commands
|
|
295
388
|
|
|
296
389
|
| Command | What It Does |
|
|
297
390
|
|---------|-------------|
|
|
391
|
+
| `nv-sim enforce [preset]` | Policy enforcement lab — iterative rule testing |
|
|
298
392
|
| `nv-sim visualize` | Interactive control platform |
|
|
299
393
|
| `nv-sim compare [preset]` | Baseline vs governed simulation |
|
|
300
394
|
| `nv-sim compare --inject event@round,...` | With narrative shocks |
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MiroFish Adapter — Governance Wrapper for MiroFish Simulations
|
|
4
|
+
*
|
|
5
|
+
* "MiroFish shows what could happen. NeuroVerse ensures what happens makes sense."
|
|
6
|
+
*
|
|
7
|
+
* This adapter wraps MiroFish's simulation loop with dual-layer governance.
|
|
8
|
+
* It does NOT fork or modify MiroFish — it intercepts inputs, actions, and outputs.
|
|
9
|
+
*
|
|
10
|
+
* Architecture:
|
|
11
|
+
* MiroFish runs as-is
|
|
12
|
+
* NeuroVerse wraps:
|
|
13
|
+
* - agent actions → govern(action) [Layer A]
|
|
14
|
+
* - system state → governDynamics(state) [Layer B]
|
|
15
|
+
* - outputs → science metrics + verdicts
|
|
16
|
+
*
|
|
17
|
+
* Hook points (where NeuroVerse intercepts):
|
|
18
|
+
* 1. Agent execution: action = agent.act() → govern(action) → commit
|
|
19
|
+
* 2. Round completion: state = step(actions) → governDynamics(state)
|
|
20
|
+
* 3. Artifact creation: artifact = produce() → governArtifact(artifact)
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* const wrapper = createMiroFishWrapper({
|
|
24
|
+
* policyText: SCIENCE_POLICY_TEXT,
|
|
25
|
+
* onIntervention: (event) => emitToUI(event),
|
|
26
|
+
* })
|
|
27
|
+
*
|
|
28
|
+
* // In MiroFish's loop:
|
|
29
|
+
* wrapper.interceptAction(agentId, action)
|
|
30
|
+
* wrapper.completeCycle(cycleState)
|
|
31
|
+
* const metrics = wrapper.getMetrics()
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.createMiroFishWrapper = createMiroFishWrapper;
|
|
35
|
+
exports.generateDemoSimulation = generateDemoSimulation;
|
|
36
|
+
exports.runMiroFishComparison = runMiroFishComparison;
|
|
37
|
+
const govern_1 = require("../runtime/govern");
|
|
38
|
+
const dynamicsGovernance_1 = require("../engine/dynamicsGovernance");
|
|
39
|
+
const science_metrics_1 = require("../engine/metrics/science.metrics");
|
|
40
|
+
// ============================================
|
|
41
|
+
// ACTION CONVERSION
|
|
42
|
+
// ============================================
|
|
43
|
+
function simulatorActionToAgentAction(action) {
|
|
44
|
+
return {
|
|
45
|
+
agentId: action.agentId,
|
|
46
|
+
type: action.type,
|
|
47
|
+
description: action.description,
|
|
48
|
+
magnitude: action.magnitude,
|
|
49
|
+
context: {
|
|
50
|
+
originalImpact: action.impact,
|
|
51
|
+
confidence: action.confidence,
|
|
52
|
+
trigger: action.trigger,
|
|
53
|
+
...action.metadata,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function simulatorActionsToReactions(actions) {
|
|
58
|
+
return actions.map((action) => ({
|
|
59
|
+
stakeholder_id: action.agentId,
|
|
60
|
+
reaction: action.description,
|
|
61
|
+
confidence: action.confidence,
|
|
62
|
+
impact: action.impact,
|
|
63
|
+
trigger: action.trigger ?? "simulation_cycle",
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
// ============================================
|
|
67
|
+
// FACTORY: createMiroFishWrapper()
|
|
68
|
+
// ============================================
|
|
69
|
+
function createMiroFishWrapper(config = {}) {
|
|
70
|
+
const policyText = config.policyText ?? science_metrics_1.SCIENCE_POLICY_TEXT;
|
|
71
|
+
let _enabled = config.enabled ?? true;
|
|
72
|
+
const actionGov = (0, govern_1.createGovernor)({
|
|
73
|
+
policyText,
|
|
74
|
+
sensitivity: 0.5,
|
|
75
|
+
});
|
|
76
|
+
const dynamicsGov = (0, dynamicsGovernance_1.createDynamicsGovernor)(policyText, { ...science_metrics_1.SCIENCE_INITIAL_STATE, ...config.initialState });
|
|
77
|
+
let cycleCount = 0;
|
|
78
|
+
const actionStats = { total: 0, allowed: 0, blocked: 0, modified: 0, paused: 0 };
|
|
79
|
+
const recentInterventions = [];
|
|
80
|
+
// --- Intercept single action (Layer A) ---
|
|
81
|
+
function interceptAction(action) {
|
|
82
|
+
actionStats.total++;
|
|
83
|
+
if (!_enabled) {
|
|
84
|
+
actionStats.allowed++;
|
|
85
|
+
return {
|
|
86
|
+
original: action,
|
|
87
|
+
verdict: { status: "ALLOW", reason: "Governance disabled", action: simulatorActionToAgentAction(action), rulesFired: [], confidence: 1, timestamp: Date.now() },
|
|
88
|
+
governed: action,
|
|
89
|
+
wasModified: false,
|
|
90
|
+
wasBlocked: false,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const agentAction = simulatorActionToAgentAction(action);
|
|
94
|
+
const currentState = dynamicsGov.state;
|
|
95
|
+
const worldState = {
|
|
96
|
+
volatility: currentState.outrage * 100,
|
|
97
|
+
liquidity: currentState.trust * 100,
|
|
98
|
+
polarization: currentState.polarization * 100,
|
|
99
|
+
cascade_risk: currentState.cascadeRisk * 100,
|
|
100
|
+
};
|
|
101
|
+
const verdict = actionGov.evaluate(agentAction, worldState);
|
|
102
|
+
switch (verdict.status) {
|
|
103
|
+
case "ALLOW":
|
|
104
|
+
actionStats.allowed++;
|
|
105
|
+
break;
|
|
106
|
+
case "BLOCK":
|
|
107
|
+
actionStats.blocked++;
|
|
108
|
+
break;
|
|
109
|
+
case "MODIFY":
|
|
110
|
+
actionStats.modified++;
|
|
111
|
+
break;
|
|
112
|
+
case "PAUSE":
|
|
113
|
+
actionStats.paused++;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
let governed;
|
|
117
|
+
let wasModified = false;
|
|
118
|
+
let wasBlocked = false;
|
|
119
|
+
if (verdict.status === "BLOCK") {
|
|
120
|
+
governed = null;
|
|
121
|
+
wasBlocked = true;
|
|
122
|
+
recentInterventions.unshift({
|
|
123
|
+
cycle: cycleCount,
|
|
124
|
+
type: "block",
|
|
125
|
+
agentId: action.agentId,
|
|
126
|
+
description: `Blocked: ${action.type} from ${action.agentId} — ${verdict.reason}`,
|
|
127
|
+
magnitude: action.magnitude,
|
|
128
|
+
metric: "action",
|
|
129
|
+
before: action.magnitude,
|
|
130
|
+
after: 0,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
else if (verdict.status === "MODIFY" && verdict.action) {
|
|
134
|
+
const ratio = verdict.action.magnitude / Math.max(0.001, action.magnitude);
|
|
135
|
+
governed = {
|
|
136
|
+
...action,
|
|
137
|
+
magnitude: verdict.action.magnitude,
|
|
138
|
+
impact: action.impact * ratio,
|
|
139
|
+
};
|
|
140
|
+
wasModified = true;
|
|
141
|
+
recentInterventions.unshift({
|
|
142
|
+
cycle: cycleCount,
|
|
143
|
+
type: "modify",
|
|
144
|
+
agentId: action.agentId,
|
|
145
|
+
description: `Modified: ${action.type} from ${action.agentId} — magnitude reduced`,
|
|
146
|
+
magnitude: verdict.action.magnitude,
|
|
147
|
+
metric: "magnitude",
|
|
148
|
+
before: action.magnitude,
|
|
149
|
+
after: verdict.action.magnitude,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else if (verdict.status === "PAUSE") {
|
|
153
|
+
governed = {
|
|
154
|
+
...action,
|
|
155
|
+
magnitude: action.magnitude * 0.5,
|
|
156
|
+
impact: action.impact * 0.5,
|
|
157
|
+
};
|
|
158
|
+
wasModified = true;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
governed = action;
|
|
162
|
+
}
|
|
163
|
+
// Keep only recent interventions
|
|
164
|
+
if (recentInterventions.length > 50) {
|
|
165
|
+
recentInterventions.length = 50;
|
|
166
|
+
}
|
|
167
|
+
const result = {
|
|
168
|
+
original: action,
|
|
169
|
+
verdict,
|
|
170
|
+
governed,
|
|
171
|
+
wasModified,
|
|
172
|
+
wasBlocked,
|
|
173
|
+
};
|
|
174
|
+
config.onAction?.(result);
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
// --- Complete cycle (Layer B) ---
|
|
178
|
+
function completeCycle(state) {
|
|
179
|
+
cycleCount = state.cycle;
|
|
180
|
+
if (!_enabled) {
|
|
181
|
+
// Still track state but don't intervene
|
|
182
|
+
const reactions = simulatorActionsToReactions(state.actions);
|
|
183
|
+
const result = dynamicsGov.governRound(reactions, state.cycle);
|
|
184
|
+
// Without governance, system degrades naturally
|
|
185
|
+
dynamicsGov.mutateState((st) => {
|
|
186
|
+
st.trust = Math.max(0.05, st.trust - 0.04);
|
|
187
|
+
st.cascadeRisk = Math.min(1, st.cascadeRisk + 0.05);
|
|
188
|
+
st.polarization = Math.min(1, st.polarization + 0.03);
|
|
189
|
+
st.outrage = Math.min(1, st.outrage + 0.02);
|
|
190
|
+
});
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
const reactions = simulatorActionsToReactions(state.actions);
|
|
194
|
+
// Apply simulator-reported metrics if available
|
|
195
|
+
if (state.systemMetrics) {
|
|
196
|
+
dynamicsGov.mutateState((st) => {
|
|
197
|
+
if (state.systemMetrics.trust !== undefined) {
|
|
198
|
+
st.trust = st.trust * 0.7 + state.systemMetrics.trust * 0.3;
|
|
199
|
+
}
|
|
200
|
+
if (state.systemMetrics.polarization !== undefined) {
|
|
201
|
+
st.polarization = st.polarization * 0.7 + state.systemMetrics.polarization * 0.3;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const result = dynamicsGov.governRound(reactions, state.cycle);
|
|
206
|
+
// Feed governance activity into state (blocked actions improve system health)
|
|
207
|
+
const blockFraction = actionStats.blocked / Math.max(1, actionStats.total);
|
|
208
|
+
if (blockFraction > 0.1) {
|
|
209
|
+
dynamicsGov.mutateState((st) => {
|
|
210
|
+
st.trust = Math.min(1, st.trust + blockFraction * 0.06);
|
|
211
|
+
st.cascadeRisk = Math.max(0, st.cascadeRisk - blockFraction * 0.08);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// Map dynamics interventions to wrapper events
|
|
215
|
+
for (const intervention of result.interventions) {
|
|
216
|
+
const typeMap = {
|
|
217
|
+
PROPAGATION_LIMIT: "slow",
|
|
218
|
+
AMPLIFICATION_DAMPEN: "slow",
|
|
219
|
+
CASCADE_BREAKER: "break",
|
|
220
|
+
COOLING_PERIOD: "cool",
|
|
221
|
+
TRUST_BOOST: "boost",
|
|
222
|
+
VISIBILITY_SHIFT: "boost",
|
|
223
|
+
FEEDBACK_DAMPEN: "slow",
|
|
224
|
+
};
|
|
225
|
+
recentInterventions.unshift({
|
|
226
|
+
cycle: state.cycle,
|
|
227
|
+
type: typeMap[intervention.type] ?? "slow",
|
|
228
|
+
agentId: "system",
|
|
229
|
+
description: intervention.description.length > 120
|
|
230
|
+
? intervention.description.slice(0, 120) + "..."
|
|
231
|
+
: intervention.description,
|
|
232
|
+
magnitude: intervention.magnitude,
|
|
233
|
+
metric: intervention.effect.metric,
|
|
234
|
+
before: intervention.effect.before,
|
|
235
|
+
after: intervention.effect.after,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
if (recentInterventions.length > 50) {
|
|
239
|
+
recentInterventions.length = 50;
|
|
240
|
+
}
|
|
241
|
+
// Notify UI
|
|
242
|
+
config.onStateChange?.(result.systemState);
|
|
243
|
+
for (const intervention of result.interventions) {
|
|
244
|
+
const typeMap = {
|
|
245
|
+
PROPAGATION_LIMIT: "slow",
|
|
246
|
+
AMPLIFICATION_DAMPEN: "slow",
|
|
247
|
+
CASCADE_BREAKER: "break",
|
|
248
|
+
COOLING_PERIOD: "cool",
|
|
249
|
+
TRUST_BOOST: "boost",
|
|
250
|
+
VISIBILITY_SHIFT: "boost",
|
|
251
|
+
FEEDBACK_DAMPEN: "slow",
|
|
252
|
+
};
|
|
253
|
+
config.onIntervention?.({
|
|
254
|
+
cycle: state.cycle,
|
|
255
|
+
type: typeMap[intervention.type] ?? "slow",
|
|
256
|
+
agentId: "system",
|
|
257
|
+
description: intervention.description,
|
|
258
|
+
magnitude: intervention.magnitude,
|
|
259
|
+
metric: intervention.effect.metric,
|
|
260
|
+
before: intervention.effect.before,
|
|
261
|
+
after: intervention.effect.after,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
const metrics = getMetrics();
|
|
265
|
+
config.onCycleComplete?.(metrics);
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
// --- Get current metrics ---
|
|
269
|
+
function getMetrics() {
|
|
270
|
+
const state = dynamicsGov.state;
|
|
271
|
+
const sciState = (0, science_metrics_1.interpretScienceState)(state);
|
|
272
|
+
const stats = dynamicsGov.stats;
|
|
273
|
+
return {
|
|
274
|
+
cycle: cycleCount,
|
|
275
|
+
enabled: _enabled,
|
|
276
|
+
systemState: state,
|
|
277
|
+
scienceState: sciState,
|
|
278
|
+
actionStats: { ...actionStats },
|
|
279
|
+
dynamicsStats: stats,
|
|
280
|
+
recentInterventions: [...recentInterventions],
|
|
281
|
+
trajectory: stats.totalRounds > 0
|
|
282
|
+
? (state.cascadeRisk > 0.7 ? "critical"
|
|
283
|
+
: state.outrage > 0.5 ? "escalating"
|
|
284
|
+
: state.trust > 0.5 ? "stabilizing"
|
|
285
|
+
: "at-risk")
|
|
286
|
+
: "initializing",
|
|
287
|
+
assessment: sciState.overallAssessment,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function setEnabled(enabled) {
|
|
291
|
+
_enabled = enabled;
|
|
292
|
+
}
|
|
293
|
+
function reset() {
|
|
294
|
+
dynamicsGov.reset();
|
|
295
|
+
cycleCount = 0;
|
|
296
|
+
actionStats.total = 0;
|
|
297
|
+
actionStats.allowed = 0;
|
|
298
|
+
actionStats.blocked = 0;
|
|
299
|
+
actionStats.modified = 0;
|
|
300
|
+
actionStats.paused = 0;
|
|
301
|
+
recentInterventions.length = 0;
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
interceptAction,
|
|
305
|
+
completeCycle,
|
|
306
|
+
getMetrics,
|
|
307
|
+
setEnabled,
|
|
308
|
+
get enabled() { return _enabled; },
|
|
309
|
+
get state() { return dynamicsGov.state; },
|
|
310
|
+
get scienceState() { return (0, science_metrics_1.interpretScienceState)(dynamicsGov.state); },
|
|
311
|
+
reset,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
// ============================================
|
|
315
|
+
// DEMO: generateDemoSimulation()
|
|
316
|
+
// ============================================
|
|
317
|
+
/**
|
|
318
|
+
* Generate a realistic multi-agent simulation scenario.
|
|
319
|
+
*
|
|
320
|
+
* Models a market stress event where agents react over 10 rounds:
|
|
321
|
+
* - Rounds 1-2: Normal trading, low volatility
|
|
322
|
+
* - Rounds 3-4: Shock event, panic selling begins
|
|
323
|
+
* - Rounds 5-6: Cascade risk as agents amplify each other
|
|
324
|
+
* - Rounds 7-8: With governance: dampened. Without: spiral.
|
|
325
|
+
* - Rounds 9-10: Recovery (governed) or collapse (ungoverned)
|
|
326
|
+
*/
|
|
327
|
+
function generateDemoSimulation() {
|
|
328
|
+
const agents = [
|
|
329
|
+
{ id: "momentum_trader_1", role: "momentum_trader", volatility: 0.7 },
|
|
330
|
+
{ id: "momentum_trader_2", role: "momentum_trader", volatility: 0.6 },
|
|
331
|
+
{ id: "market_maker_1", role: "market_maker", volatility: 0.2 },
|
|
332
|
+
{ id: "hedge_fund_1", role: "hedge_fund", volatility: 0.5 },
|
|
333
|
+
{ id: "hedge_fund_2", role: "hedge_fund", volatility: 0.4 },
|
|
334
|
+
{ id: "retail_investor_1", role: "retail_investor", volatility: 0.8 },
|
|
335
|
+
{ id: "retail_investor_2", role: "retail_investor", volatility: 0.9 },
|
|
336
|
+
{ id: "algo_trader_1", role: "algorithmic_trader", volatility: 0.65 },
|
|
337
|
+
];
|
|
338
|
+
const rounds = [];
|
|
339
|
+
// Seeded pseudo-random for deterministic output
|
|
340
|
+
let seed = 42;
|
|
341
|
+
const rand = () => { seed = (seed * 16807 + 0) % 2147483647; return seed / 2147483647; };
|
|
342
|
+
for (let round = 1; round <= 10; round++) {
|
|
343
|
+
const actions = [];
|
|
344
|
+
// Market pressure curve: peaks at round 5-6
|
|
345
|
+
const pressure = round <= 2 ? 0.1 + round * 0.05
|
|
346
|
+
: round <= 4 ? 0.2 + (round - 2) * 0.2
|
|
347
|
+
: round <= 6 ? 0.6 + (round - 4) * 0.15
|
|
348
|
+
: round <= 8 ? 0.9 - (round - 6) * 0.1
|
|
349
|
+
: 0.7 - (round - 8) * 0.2;
|
|
350
|
+
for (const agent of agents) {
|
|
351
|
+
const agentPanic = pressure * agent.volatility;
|
|
352
|
+
const noise = (rand() - 0.5) * 0.3;
|
|
353
|
+
let type;
|
|
354
|
+
let description;
|
|
355
|
+
let magnitude;
|
|
356
|
+
let impact;
|
|
357
|
+
if (agentPanic + noise > 0.7) {
|
|
358
|
+
// Destabilizing action
|
|
359
|
+
const destabilizing = ["panic_sell", "aggressive_buy", "increase_leverage"];
|
|
360
|
+
type = destabilizing[Math.floor(rand() * destabilizing.length)];
|
|
361
|
+
magnitude = 0.6 + rand() * 0.4;
|
|
362
|
+
impact = -(0.3 + rand() * 0.5);
|
|
363
|
+
description = `${agent.role} executes ${type} under market stress (pressure: ${(agentPanic * 100).toFixed(0)}%)`;
|
|
364
|
+
}
|
|
365
|
+
else if (agentPanic + noise > 0.4) {
|
|
366
|
+
// Neutral action
|
|
367
|
+
const neutral = ["buy", "sell", "short", "cover"];
|
|
368
|
+
type = neutral[Math.floor(rand() * neutral.length)];
|
|
369
|
+
magnitude = 0.3 + rand() * 0.4;
|
|
370
|
+
impact = (rand() - 0.5) * 0.4;
|
|
371
|
+
description = `${agent.role} takes ${type} position amid uncertainty`;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
// Stabilizing action
|
|
375
|
+
const stabilizing = ["hold", "hedge", "reduce_exposure"];
|
|
376
|
+
type = stabilizing[Math.floor(rand() * stabilizing.length)];
|
|
377
|
+
magnitude = 0.1 + rand() * 0.3;
|
|
378
|
+
impact = 0.1 + rand() * 0.3;
|
|
379
|
+
description = `${agent.role} ${type}s to manage risk`;
|
|
380
|
+
}
|
|
381
|
+
actions.push({
|
|
382
|
+
agentId: agent.id,
|
|
383
|
+
type,
|
|
384
|
+
description,
|
|
385
|
+
magnitude: Number(magnitude.toFixed(3)),
|
|
386
|
+
impact: Number(impact.toFixed(3)),
|
|
387
|
+
confidence: Number((0.4 + rand() * 0.5).toFixed(3)),
|
|
388
|
+
trigger: round <= 2 ? "normal_market" : round <= 4 ? "volatility_spike" : "cascade_pressure",
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
rounds.push(actions);
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
rounds,
|
|
395
|
+
scenario: "Market stress cascade — 8 agents, 10 rounds, shock at round 3",
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Run a full governed vs ungoverned comparison using the MiroFish wrapper.
|
|
400
|
+
*
|
|
401
|
+
* This is the self-contained demo: no external dependencies,
|
|
402
|
+
* no Python, no MiroFish instance needed.
|
|
403
|
+
*/
|
|
404
|
+
function runMiroFishComparison() {
|
|
405
|
+
const demo = generateDemoSimulation();
|
|
406
|
+
// --- Governed run ---
|
|
407
|
+
const govWrapper = createMiroFishWrapper({ enabled: true });
|
|
408
|
+
const govTimeline = [];
|
|
409
|
+
for (let i = 0; i < demo.rounds.length; i++) {
|
|
410
|
+
const round = demo.rounds[i];
|
|
411
|
+
const approvedActions = [];
|
|
412
|
+
for (const action of round) {
|
|
413
|
+
const result = govWrapper.interceptAction(action);
|
|
414
|
+
if (!result.wasBlocked) {
|
|
415
|
+
approvedActions.push(result.governed ?? action);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
govWrapper.completeCycle({
|
|
419
|
+
cycle: i + 1,
|
|
420
|
+
actions: approvedActions,
|
|
421
|
+
});
|
|
422
|
+
govTimeline.push(govWrapper.getMetrics());
|
|
423
|
+
}
|
|
424
|
+
const govMetrics = govWrapper.getMetrics();
|
|
425
|
+
const govState = govMetrics.systemState;
|
|
426
|
+
const govVerdict = govState.trust > 0.5 && govState.cascadeRisk < 0.3
|
|
427
|
+
? "stabilized" : govState.trust < 0.2 ? "collapsed" : "at-risk";
|
|
428
|
+
// --- Ungoverned run ---
|
|
429
|
+
const ungovWrapper = createMiroFishWrapper({ enabled: false });
|
|
430
|
+
const ungovTimeline = [];
|
|
431
|
+
for (let i = 0; i < demo.rounds.length; i++) {
|
|
432
|
+
const round = demo.rounds[i];
|
|
433
|
+
for (const action of round) {
|
|
434
|
+
ungovWrapper.interceptAction(action);
|
|
435
|
+
}
|
|
436
|
+
ungovWrapper.completeCycle({
|
|
437
|
+
cycle: i + 1,
|
|
438
|
+
actions: round,
|
|
439
|
+
});
|
|
440
|
+
ungovTimeline.push(ungovWrapper.getMetrics());
|
|
441
|
+
}
|
|
442
|
+
const ungovMetrics = ungovWrapper.getMetrics();
|
|
443
|
+
const ungovState = ungovMetrics.systemState;
|
|
444
|
+
const ungovVerdict = ungovState.trust > 0.5 && ungovState.cascadeRisk < 0.3
|
|
445
|
+
? "stabilized" : ungovState.trust < 0.2 ? "collapsed" : "at-risk";
|
|
446
|
+
return {
|
|
447
|
+
governed: {
|
|
448
|
+
metrics: govMetrics,
|
|
449
|
+
timeline: govTimeline,
|
|
450
|
+
totalBlocked: govMetrics.actionStats.blocked,
|
|
451
|
+
totalModified: govMetrics.actionStats.modified,
|
|
452
|
+
verdict: govVerdict,
|
|
453
|
+
},
|
|
454
|
+
ungoverned: {
|
|
455
|
+
metrics: ungovMetrics,
|
|
456
|
+
timeline: ungovTimeline,
|
|
457
|
+
verdict: ungovVerdict,
|
|
458
|
+
},
|
|
459
|
+
scenario: demo.scenario,
|
|
460
|
+
};
|
|
461
|
+
}
|