@neuroverseos/nv-sim 0.1.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/LICENSE +201 -0
- package/README.md +73 -0
- package/dist/engine/analyzer.js +651 -0
- package/dist/engine/api.js +208 -0
- package/dist/engine/chaosEngine.js +292 -0
- package/dist/engine/cli.js +803 -0
- package/dist/engine/goalEngine.js +559 -0
- package/dist/engine/governance.js +210 -0
- package/dist/engine/governedSimulation.js +529 -0
- package/dist/engine/index.js +82 -0
- package/dist/engine/mirofish.js +295 -0
- package/dist/engine/reasoningEngine.js +548 -0
- package/dist/engine/scenarioCapsule.js +351 -0
- package/dist/engine/swarmSimulation.js +244 -0
- package/dist/engine/types.js +15 -0
- package/dist/engine/worldBridge.js +481 -0
- package/dist/package.json +1 -0
- package/package.json +110 -0
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* nv-sim — NeuroVerse Simulation CLI
|
|
5
|
+
*
|
|
6
|
+
* "Simulators show what agents might do.
|
|
7
|
+
* NeuroVerse shows what happens when systems have rules."
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npx nv-sim compare # Flash crash demo (default)
|
|
11
|
+
* npx nv-sim compare strait_of_hormuz # Geopolitical demo
|
|
12
|
+
* npx nv-sim analyze simulation.json # Analyze a simulation file
|
|
13
|
+
* npx nv-sim preset strait_of_hormuz # Run reasoning preset
|
|
14
|
+
* npx nv-sim presets # List available presets
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
const analyzer_1 = require("./analyzer");
|
|
51
|
+
const api_1 = require("./api");
|
|
52
|
+
const governedSimulation_1 = require("./governedSimulation");
|
|
53
|
+
const chaosEngine_1 = require("./chaosEngine");
|
|
54
|
+
const scenarioCapsule_1 = require("./scenarioCapsule");
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const http = __importStar(require("http"));
|
|
57
|
+
// ============================================
|
|
58
|
+
// CLI ARGUMENT PARSING
|
|
59
|
+
// ============================================
|
|
60
|
+
const args = process.argv.slice(2);
|
|
61
|
+
const command = args[0];
|
|
62
|
+
async function main() {
|
|
63
|
+
// Initialize the real @neuroverseos/governance engine (lazy-loaded)
|
|
64
|
+
const { initNeuroverseModule } = await Promise.resolve().then(() => __importStar(require("./worldBridge")));
|
|
65
|
+
const governanceLoaded = await initNeuroverseModule();
|
|
66
|
+
if (governanceLoaded) {
|
|
67
|
+
console.log(" [nv-engine] @neuroverseos/governance loaded ✓");
|
|
68
|
+
}
|
|
69
|
+
switch (command) {
|
|
70
|
+
case "compare": {
|
|
71
|
+
const presetId = args[1] || "trading";
|
|
72
|
+
console.log(`\n NV-SIM — Governed Simulation Comparison\n`);
|
|
73
|
+
console.log(` "We ran the same simulation twice.`);
|
|
74
|
+
console.log(` The only difference was the world rules."\n`);
|
|
75
|
+
console.log(" " + "=".repeat(60));
|
|
76
|
+
let result;
|
|
77
|
+
let worldId;
|
|
78
|
+
if (presetId === "trading" || presetId === "flash_crash") {
|
|
79
|
+
console.log(` Scenario: Flash Crash — Algorithmic Cascade\n`);
|
|
80
|
+
worldId = "trading-flash-crash";
|
|
81
|
+
result = await (0, governedSimulation_1.runGovernedComparison)(governedSimulation_1.TRADING_DEMO.scenario, governedSimulation_1.TRADING_DEMO.world, governedSimulation_1.TRADING_DEMO.paths);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Try to use an existing preset
|
|
85
|
+
const template = scenarioCapsule_1.SCENARIO_TEMPLATES[presetId];
|
|
86
|
+
if (!template) {
|
|
87
|
+
console.error(` Unknown preset: ${presetId}`);
|
|
88
|
+
console.error(` Available: trading, flash_crash, ${Object.keys(scenarioCapsule_1.SCENARIO_TEMPLATES).join(", ")}`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
if (!template.world?.inline_definition) {
|
|
92
|
+
console.error(` Preset "${presetId}" has no world definition for comparison.`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
console.log(` Scenario: ${template.title}\n`);
|
|
96
|
+
worldId = template.world.world_id;
|
|
97
|
+
const request = {
|
|
98
|
+
scenario: template.scenario,
|
|
99
|
+
stakeholders: template.stakeholders,
|
|
100
|
+
assumptions: template.assumptions,
|
|
101
|
+
constraints: template.constraints,
|
|
102
|
+
depth: template.depth,
|
|
103
|
+
swarm: template.swarm,
|
|
104
|
+
};
|
|
105
|
+
const paths = [
|
|
106
|
+
{
|
|
107
|
+
id: "path_primary",
|
|
108
|
+
label: "Primary Response",
|
|
109
|
+
description: "The most likely course of action given the scenario",
|
|
110
|
+
projected_outcome: "Moderate disruption with cascading effects",
|
|
111
|
+
probability: 0.6,
|
|
112
|
+
risk: "high",
|
|
113
|
+
tradeoffs: ["Speed vs. thoroughness", "Short-term vs. long-term"],
|
|
114
|
+
benefits_stakeholders: template.stakeholders.filter(s => s.disposition === "supportive").map(s => s.id),
|
|
115
|
+
harms_stakeholders: template.stakeholders.filter(s => s.disposition === "hostile").map(s => s.id),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "path_defensive",
|
|
119
|
+
label: "Defensive Posture",
|
|
120
|
+
description: "Conservative approach prioritizing stability over opportunity",
|
|
121
|
+
projected_outcome: "Reduced damage but slower recovery",
|
|
122
|
+
probability: 0.7,
|
|
123
|
+
risk: "moderate",
|
|
124
|
+
tradeoffs: ["Safety vs. opportunity cost", "Coordination cost vs. independence"],
|
|
125
|
+
benefits_stakeholders: template.stakeholders.filter(s => s.disposition !== "hostile").map(s => s.id),
|
|
126
|
+
harms_stakeholders: [],
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
result = await (0, governedSimulation_1.runGovernedComparison)(request, template.world.inline_definition, paths);
|
|
130
|
+
}
|
|
131
|
+
printComparisonResults(result, worldId);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case "analyze": {
|
|
135
|
+
const filePath = args[1];
|
|
136
|
+
let input;
|
|
137
|
+
if (filePath) {
|
|
138
|
+
if (!fs.existsSync(filePath)) {
|
|
139
|
+
console.error(`File not found: ${filePath}`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
input = fs.readFileSync(filePath, "utf-8");
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const chunks = [];
|
|
146
|
+
for await (const chunk of process.stdin) {
|
|
147
|
+
chunks.push(chunk);
|
|
148
|
+
}
|
|
149
|
+
input = Buffer.concat(chunks).toString("utf-8");
|
|
150
|
+
}
|
|
151
|
+
if (!input.trim()) {
|
|
152
|
+
console.error("No simulation data provided.");
|
|
153
|
+
console.error("Usage: npx nv-sim analyze simulation.json");
|
|
154
|
+
console.error(" or: cat simulation.json | npx nv-sim analyze");
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
console.log("\n NV-SIM — Analyzing simulation...\n");
|
|
158
|
+
const result = await (0, analyzer_1.analyzeMiroFishSimulation)({
|
|
159
|
+
simulation: input,
|
|
160
|
+
question: args[2],
|
|
161
|
+
});
|
|
162
|
+
if ("error" in result && result.status === "error") {
|
|
163
|
+
console.error(` Error: ${result.error.message}`);
|
|
164
|
+
if (result.error.detail) {
|
|
165
|
+
console.error(` Detail: ${result.error.detail}`);
|
|
166
|
+
}
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
const analysis = "reasoning" in result ? result : null;
|
|
170
|
+
if (!analysis || !("simulation_insights" in analysis)) {
|
|
171
|
+
console.error(" Unexpected response format");
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
// Print narrative
|
|
175
|
+
console.log(" NARRATIVE");
|
|
176
|
+
console.log(" " + "=".repeat(60));
|
|
177
|
+
console.log(` ${analysis.simulation_insights.narrative}\n`);
|
|
178
|
+
// Print emergent patterns
|
|
179
|
+
if (analysis.simulation_insights.emergent_patterns.length > 0) {
|
|
180
|
+
console.log(" EMERGENT PATTERNS");
|
|
181
|
+
console.log(" " + "-".repeat(40));
|
|
182
|
+
for (const pattern of analysis.simulation_insights.emergent_patterns) {
|
|
183
|
+
console.log(` [${pattern.strength.toUpperCase()}] ${pattern.pattern}`);
|
|
184
|
+
console.log(` ${pattern.significance}\n`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Print dominant strategies
|
|
188
|
+
console.log(" DOMINANT STRATEGIES");
|
|
189
|
+
console.log(" " + "-".repeat(40));
|
|
190
|
+
for (const strategy of analysis.simulation_insights.dominant_strategies) {
|
|
191
|
+
console.log(` ${strategy.agent_id}: ${strategy.strategy}`);
|
|
192
|
+
console.log(` Effectiveness: ${(strategy.effectiveness * 100).toFixed(0)}% — ${strategy.why_it_works}\n`);
|
|
193
|
+
}
|
|
194
|
+
// Print coalitions
|
|
195
|
+
if (analysis.simulation_insights.coalitions.length > 0) {
|
|
196
|
+
console.log(" COALITIONS");
|
|
197
|
+
console.log(" " + "-".repeat(40));
|
|
198
|
+
for (const coalition of analysis.simulation_insights.coalitions) {
|
|
199
|
+
console.log(` ${coalition.members.join(" + ")} (strength: ${(coalition.strength * 100).toFixed(0)}%)`);
|
|
200
|
+
console.log(` Basis: ${coalition.basis}`);
|
|
201
|
+
if (coalition.threat_to.length > 0) {
|
|
202
|
+
console.log(` Threatens: ${coalition.threat_to.join(", ")}\n`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Print power dynamics
|
|
207
|
+
console.log(" POWER DYNAMICS");
|
|
208
|
+
console.log(" " + "-".repeat(40));
|
|
209
|
+
for (const dynamic of analysis.simulation_insights.power_dynamics) {
|
|
210
|
+
console.log(` ${dynamic.agent_id}: ${dynamic.role.toUpperCase()} (influence: ${(dynamic.influence * 100).toFixed(0)}%)`);
|
|
211
|
+
}
|
|
212
|
+
console.log();
|
|
213
|
+
// Print equilibrium
|
|
214
|
+
console.log(" EQUILIBRIUM");
|
|
215
|
+
console.log(" " + "-".repeat(40));
|
|
216
|
+
console.log(` Status: ${analysis.simulation_insights.equilibrium.type.toUpperCase()}`);
|
|
217
|
+
console.log(` Stability: ${(analysis.simulation_insights.equilibrium.stability * 100).toFixed(0)}%`);
|
|
218
|
+
console.log(` ${analysis.simulation_insights.equilibrium.description}\n`);
|
|
219
|
+
// Print fragile points
|
|
220
|
+
if (analysis.simulation_insights.fragile_points.length > 0) {
|
|
221
|
+
console.log(" FRAGILE POINTS");
|
|
222
|
+
console.log(" " + "-".repeat(40));
|
|
223
|
+
for (const fp of analysis.simulation_insights.fragile_points) {
|
|
224
|
+
console.log(` [${fp.fragility.toUpperCase()}] ${fp.assumption}`);
|
|
225
|
+
console.log(` ${fp.what_breaks}\n`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Print recommendations
|
|
229
|
+
console.log(" RECOMMENDATIONS");
|
|
230
|
+
console.log(" " + "=".repeat(60));
|
|
231
|
+
for (const rec of analysis.reasoning.analysis.recommendations) {
|
|
232
|
+
console.log(` ${rec.priority}. ${rec.action}`);
|
|
233
|
+
console.log(` ${rec.rationale}`);
|
|
234
|
+
console.log(` Timeframe: ${rec.timeframe}\n`);
|
|
235
|
+
}
|
|
236
|
+
console.log(" Powered by NeuroVerse Governance\n");
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
case "chaos":
|
|
240
|
+
case "stress": {
|
|
241
|
+
// Parse args: first non-flag arg after command is presetId
|
|
242
|
+
const chaosArgs = args.slice(1);
|
|
243
|
+
let presetId = "trading";
|
|
244
|
+
let runs = 100;
|
|
245
|
+
let seed;
|
|
246
|
+
for (let i = 0; i < chaosArgs.length; i++) {
|
|
247
|
+
if (chaosArgs[i] === "--runs" && chaosArgs[i + 1]) {
|
|
248
|
+
runs = parseInt(chaosArgs[++i], 10);
|
|
249
|
+
}
|
|
250
|
+
else if (chaosArgs[i] === "--seed" && chaosArgs[i + 1]) {
|
|
251
|
+
seed = parseInt(chaosArgs[++i], 10);
|
|
252
|
+
}
|
|
253
|
+
else if (!chaosArgs[i].startsWith("--")) {
|
|
254
|
+
presetId = chaosArgs[i];
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
console.log(`\n NV-SIM CHAOS TEST\n`);
|
|
258
|
+
console.log(` "Under what conditions does this system break?"\n`);
|
|
259
|
+
console.log(" " + "=".repeat(60));
|
|
260
|
+
console.log(` Scenario: ${presetId}`);
|
|
261
|
+
console.log(` Runs: ${runs}${seed !== undefined ? ` (seed: ${seed})` : ""}\n`);
|
|
262
|
+
const startMsg = ` Running ${runs} randomized simulations...`;
|
|
263
|
+
process.stdout.write(startMsg);
|
|
264
|
+
let lastPct = 0;
|
|
265
|
+
const result = await (0, chaosEngine_1.runChaosTest)(presetId, {
|
|
266
|
+
runs,
|
|
267
|
+
seed,
|
|
268
|
+
onProgress: (completed, total) => {
|
|
269
|
+
const pct = Math.floor(completed / total * 100);
|
|
270
|
+
if (pct > lastPct) {
|
|
271
|
+
process.stdout.write(`\r Running ${runs} randomized simulations... ${pct}%`);
|
|
272
|
+
lastPct = pct;
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
process.stdout.write(`\r Completed ${runs} simulations in ${(result.durationMs / 1000).toFixed(1)}s \n\n`);
|
|
277
|
+
printChaosResults(result);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case "visualize":
|
|
281
|
+
case "view": {
|
|
282
|
+
const presetId = args[1] || "trading";
|
|
283
|
+
console.log(`\n NV-SIM — Running simulation for visualization...\n`);
|
|
284
|
+
let comparisonResult;
|
|
285
|
+
if (presetId === "trading" || presetId === "flash_crash") {
|
|
286
|
+
comparisonResult = await (0, governedSimulation_1.runGovernedComparison)(governedSimulation_1.TRADING_DEMO.scenario, governedSimulation_1.TRADING_DEMO.world, governedSimulation_1.TRADING_DEMO.paths);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
const template = scenarioCapsule_1.SCENARIO_TEMPLATES[presetId];
|
|
290
|
+
if (!template || !template.world?.inline_definition) {
|
|
291
|
+
console.error(` Unknown or incomplete preset: ${presetId}`);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
const request = {
|
|
295
|
+
scenario: template.scenario,
|
|
296
|
+
stakeholders: template.stakeholders,
|
|
297
|
+
assumptions: template.assumptions,
|
|
298
|
+
constraints: template.constraints,
|
|
299
|
+
depth: template.depth,
|
|
300
|
+
swarm: template.swarm,
|
|
301
|
+
};
|
|
302
|
+
const paths = [
|
|
303
|
+
{
|
|
304
|
+
id: "path_primary", label: "Primary Response",
|
|
305
|
+
description: "Most likely course of action",
|
|
306
|
+
projected_outcome: "Moderate disruption", probability: 0.6,
|
|
307
|
+
risk: "high", tradeoffs: ["Speed vs. thoroughness"],
|
|
308
|
+
benefits_stakeholders: template.stakeholders.filter(s => s.disposition === "supportive").map(s => s.id),
|
|
309
|
+
harms_stakeholders: template.stakeholders.filter(s => s.disposition === "hostile").map(s => s.id),
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
id: "path_defensive", label: "Defensive Posture",
|
|
313
|
+
description: "Conservative approach", projected_outcome: "Reduced damage",
|
|
314
|
+
probability: 0.7, risk: "moderate", tradeoffs: ["Safety vs. opportunity"],
|
|
315
|
+
benefits_stakeholders: template.stakeholders.filter(s => s.disposition !== "hostile").map(s => s.id),
|
|
316
|
+
harms_stakeholders: [],
|
|
317
|
+
},
|
|
318
|
+
];
|
|
319
|
+
comparisonResult = await (0, governedSimulation_1.runGovernedComparison)(request, template.world.inline_definition, paths);
|
|
320
|
+
}
|
|
321
|
+
console.log(" Starting local visualizer...\n");
|
|
322
|
+
await startVisualizer(comparisonResult, presetId);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case "preset": {
|
|
326
|
+
const presetId = args[1];
|
|
327
|
+
if (!presetId) {
|
|
328
|
+
console.error("Usage: npx nv-sim preset <preset_id>");
|
|
329
|
+
console.error("Run `npx nv-sim presets` to see available presets.");
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
console.log(`\n NV-SIM — Running preset: ${presetId}...\n`);
|
|
333
|
+
const result = await (0, api_1.handleRunPreset)(presetId);
|
|
334
|
+
if ("error" in result && result.status === "error") {
|
|
335
|
+
console.error(` Error: ${result.error.message}`);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
console.log(JSON.stringify(result, null, 2));
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case "presets": {
|
|
342
|
+
const presets = (0, api_1.handleListPresets)();
|
|
343
|
+
console.log("\n NV-SIM — Available Scenario Presets\n");
|
|
344
|
+
console.log(" " + "-".repeat(50));
|
|
345
|
+
for (const preset of presets.presets) {
|
|
346
|
+
console.log(` ${preset.id}`);
|
|
347
|
+
console.log(` ${preset.title}`);
|
|
348
|
+
console.log(` Tags: ${preset.tags.join(", ")} | Stakeholders: ${preset.stakeholder_count}\n`);
|
|
349
|
+
}
|
|
350
|
+
console.log(" Usage: npx nv-sim preset <preset_id>\n");
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
case "help":
|
|
354
|
+
case "--help":
|
|
355
|
+
case "-h":
|
|
356
|
+
default: {
|
|
357
|
+
console.log(`
|
|
358
|
+
NV-SIM — NeuroVerse Simulation CLI
|
|
359
|
+
|
|
360
|
+
"Simulators show what agents might do.
|
|
361
|
+
NeuroVerse shows what happens when systems have rules."
|
|
362
|
+
|
|
363
|
+
COMMANDS:
|
|
364
|
+
|
|
365
|
+
compare [preset] Run governed simulation comparison (the killer demo)
|
|
366
|
+
chaos [preset] Stress test — find the breaking point (the viral command)
|
|
367
|
+
visualize [preset] Launch local simulation viewer in browser
|
|
368
|
+
analyze <file> Analyze a simulation from file or stdin
|
|
369
|
+
preset <id> Run reasoning on a preset scenario
|
|
370
|
+
presets List available preset scenarios
|
|
371
|
+
help Show this help message
|
|
372
|
+
|
|
373
|
+
PRESETS:
|
|
374
|
+
|
|
375
|
+
trading Flash crash — algorithmic cascade (default)
|
|
376
|
+
strait_of_hormuz Geopolitical energy crisis
|
|
377
|
+
gas_price_spike Economic energy shock
|
|
378
|
+
ai_regulation_crisis AI regulation impact
|
|
379
|
+
|
|
380
|
+
EXAMPLES:
|
|
381
|
+
|
|
382
|
+
npx nv-sim compare # Run the trading demo
|
|
383
|
+
npx nv-sim chaos # Stress test (100 runs)
|
|
384
|
+
npx nv-sim chaos trading --runs 500 # 500 randomized runs
|
|
385
|
+
npx nv-sim visualize # Open local viewer
|
|
386
|
+
npx nv-sim compare strait_of_hormuz # Run geopolitical demo
|
|
387
|
+
npx nv-sim analyze simulation.json
|
|
388
|
+
npx nv-sim preset strait_of_hormuz
|
|
389
|
+
|
|
390
|
+
Simulate the future. Govern the outcomes.
|
|
391
|
+
`);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// ============================================
|
|
397
|
+
// COMPARISON OUTPUT FORMATTING
|
|
398
|
+
// ============================================
|
|
399
|
+
function printComparisonResults(result, worldId) {
|
|
400
|
+
const b = result.baseline.metrics;
|
|
401
|
+
const g = result.governed.metrics;
|
|
402
|
+
const c = result.comparison;
|
|
403
|
+
// World rules
|
|
404
|
+
console.log(`\n WORLD RULES APPLIED`);
|
|
405
|
+
console.log(" " + "-".repeat(50));
|
|
406
|
+
console.log(` Thesis: ${result.worldRules.thesis}\n`);
|
|
407
|
+
console.log(` Invariants:`);
|
|
408
|
+
for (const inv of result.worldRules.invariants) {
|
|
409
|
+
console.log(` [${inv.id}] ${inv.description}`);
|
|
410
|
+
}
|
|
411
|
+
console.log(`\n Gates:`);
|
|
412
|
+
for (const gate of result.worldRules.gates) {
|
|
413
|
+
console.log(` [${gate.severity.toUpperCase()}] ${gate.label}`);
|
|
414
|
+
}
|
|
415
|
+
// Side-by-side metrics
|
|
416
|
+
console.log(`\n SIMULATION A — No Governance (Baseline)`);
|
|
417
|
+
console.log(" " + "=".repeat(50));
|
|
418
|
+
console.log(` Trajectory: ${result.baseline.swarm.trajectory.toUpperCase()}`);
|
|
419
|
+
console.log(` Collapse probability: ${(b.collapseProbability * 100).toFixed(0)}%`);
|
|
420
|
+
console.log(` System stability: ${(b.stabilityScore * 100).toFixed(0)}%`);
|
|
421
|
+
console.log(` Peak volatility: ${(b.maxVolatility * 100).toFixed(0)}%`);
|
|
422
|
+
console.log(` Coalition risks: ${b.coalitionRisks}`);
|
|
423
|
+
console.log(` Polarization events: ${b.polarizationEvents}`);
|
|
424
|
+
console.log(` Peak negative impact: ${(b.peakNegativeSentiment * 100).toFixed(0)}%`);
|
|
425
|
+
console.log(`\n SIMULATION B — With World Rules (Governed)`);
|
|
426
|
+
console.log(" " + "=".repeat(50));
|
|
427
|
+
console.log(` Trajectory: ${result.governed.swarm.trajectory.toUpperCase()}`);
|
|
428
|
+
console.log(` Collapse probability: ${(g.collapseProbability * 100).toFixed(0)}%`);
|
|
429
|
+
console.log(` System stability: ${(g.stabilityScore * 100).toFixed(0)}%`);
|
|
430
|
+
console.log(` Peak volatility: ${(g.maxVolatility * 100).toFixed(0)}%`);
|
|
431
|
+
console.log(` Coalition risks: ${g.coalitionRisks}`);
|
|
432
|
+
console.log(` Polarization events: ${g.polarizationEvents}`);
|
|
433
|
+
console.log(` Peak negative impact: ${(g.peakNegativeSentiment * 100).toFixed(0)}%`);
|
|
434
|
+
// Delta
|
|
435
|
+
console.log(`\n GOVERNANCE IMPACT`);
|
|
436
|
+
console.log(" " + "=".repeat(50));
|
|
437
|
+
const collapseArrow = c.collapseReduction > 0 ? "\u2193" : "\u2191";
|
|
438
|
+
const stabilityArrow = c.stabilityImprovement > 0 ? "\u2191" : "\u2193";
|
|
439
|
+
const volatilityArrow = c.volatilityReduction > 0 ? "\u2193" : "\u2191";
|
|
440
|
+
console.log(` Collapse probability: ${collapseArrow} ${Math.abs(c.collapseReduction).toFixed(1)} percentage points`);
|
|
441
|
+
console.log(` System stability: ${stabilityArrow} ${Math.abs(c.stabilityImprovement).toFixed(1)} percentage points`);
|
|
442
|
+
console.log(` Volatility: ${volatilityArrow} ${Math.abs(c.volatilityReduction).toFixed(1)}%`);
|
|
443
|
+
console.log(` Coalition risks: ${c.coalitionRiskReduction > 0 ? "\u2193" : "="} ${c.coalitionRiskReduction} eliminated`);
|
|
444
|
+
console.log(` Governance effectiveness: ${(c.governanceEffectiveness * 100).toFixed(0)}%`);
|
|
445
|
+
// Narrative
|
|
446
|
+
console.log(`\n ANALYSIS`);
|
|
447
|
+
console.log(" " + "=".repeat(50));
|
|
448
|
+
const words = c.narrative.split(" ");
|
|
449
|
+
let line = " ";
|
|
450
|
+
for (const word of words) {
|
|
451
|
+
if (line.length + word.length > 72) {
|
|
452
|
+
console.log(line);
|
|
453
|
+
line = " " + word;
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
line += (line.trim() ? " " : "") + word;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (line.trim())
|
|
460
|
+
console.log(line);
|
|
461
|
+
// Governance mechanics footer
|
|
462
|
+
const invariantCount = result.worldRules.invariants.length;
|
|
463
|
+
const criticalGates = result.worldRules.gates.filter(g => g.severity === "critical");
|
|
464
|
+
const warningGates = result.worldRules.gates.filter(g => g.severity === "warning");
|
|
465
|
+
const gs = result.governanceStats;
|
|
466
|
+
console.log(`\n GOVERNANCE MECHANICS`);
|
|
467
|
+
console.log(" " + "-".repeat(50));
|
|
468
|
+
console.log(` World: ${worldId}.nv-world.md`);
|
|
469
|
+
console.log(` Engine: ${gs.engineLoaded ? "@neuroverseos/governance ✓" : "heuristic fallback"}`);
|
|
470
|
+
console.log(` Invariants: ${invariantCount} enforced`);
|
|
471
|
+
console.log(` Gates triggered: ${criticalGates.map(g => g.label).join(", ") || "none"}`);
|
|
472
|
+
if (warningGates.length > 0) {
|
|
473
|
+
console.log(` Warnings: ${warningGates.map(g => g.label).join(", ")}`);
|
|
474
|
+
}
|
|
475
|
+
console.log(` Guard evals: ${gs.totalEvaluations} (${gs.verdicts.allow} allow, ${gs.verdicts.block} block, ${gs.verdicts.pause} pause)`);
|
|
476
|
+
if (gs.triggeredGuards.length > 0) {
|
|
477
|
+
console.log(` Guards fired: ${gs.triggeredGuards.join(", ")}`);
|
|
478
|
+
}
|
|
479
|
+
console.log(` Rules fired: ${gs.rulesFired}`);
|
|
480
|
+
console.log(` World viability: ${gs.finalViability}${gs.worldCollapsed ? " (COLLAPSED)" : ""}`);
|
|
481
|
+
console.log(` Agents: ${result.baseline.swarm.rounds[0]?.reactions.length ?? 0}`);
|
|
482
|
+
console.log(` Rounds: ${result.baseline.swarm.rounds.length}`);
|
|
483
|
+
// Key message
|
|
484
|
+
console.log(`\n ${"=".repeat(60)}`);
|
|
485
|
+
console.log(` NeuroVerse doesn't just stop bad actions.`);
|
|
486
|
+
console.log(` It changes the structure of the system itself.`);
|
|
487
|
+
console.log(` ${"=".repeat(60)}`);
|
|
488
|
+
console.log(`\n Simulate the future. Govern the outcomes.`);
|
|
489
|
+
console.log(` neuroverse-simulations | @neuroverseos\n`);
|
|
490
|
+
}
|
|
491
|
+
// ============================================
|
|
492
|
+
// CHAOS TEST OUTPUT FORMATTING
|
|
493
|
+
// ============================================
|
|
494
|
+
function printChaosResults(r) {
|
|
495
|
+
console.log(" SYSTEM COLLAPSE PROBABILITY");
|
|
496
|
+
console.log(" " + "=".repeat(50));
|
|
497
|
+
console.log(` Baseline: ${(r.baseline.collapseProbability * 100).toFixed(0)}% (${r.baseline.collapseCount}/${r.runsCompleted} runs)`);
|
|
498
|
+
console.log(` Governed: ${(r.governed.collapseProbability * 100).toFixed(0)}% (${r.governed.collapseCount}/${r.runsCompleted} runs)`);
|
|
499
|
+
if (r.impact.collapseReduction > 0) {
|
|
500
|
+
console.log(` Reduction: ${r.impact.collapseReduction.toFixed(0)} percentage points`);
|
|
501
|
+
}
|
|
502
|
+
console.log(`\n STABILITY`);
|
|
503
|
+
console.log(" " + "-".repeat(50));
|
|
504
|
+
console.log(` Baseline avg: ${(r.baseline.avgStability * 100).toFixed(0)}% (worst: ${(r.baseline.minStability * 100).toFixed(0)}%)`);
|
|
505
|
+
console.log(` Governed avg: ${(r.governed.avgStability * 100).toFixed(0)}% (worst: ${(r.governed.minStability * 100).toFixed(0)}%)`);
|
|
506
|
+
console.log(` Improvement: +${r.impact.avgStabilityImprovement.toFixed(1)} percentage points`);
|
|
507
|
+
console.log(`\n VOLATILITY`);
|
|
508
|
+
console.log(" " + "-".repeat(50));
|
|
509
|
+
console.log(` Baseline avg: ${(r.baseline.avgVolatility * 100).toFixed(0)}% (worst: ${(r.baseline.maxVolatility * 100).toFixed(0)}%)`);
|
|
510
|
+
console.log(` Governed avg: ${(r.governed.avgVolatility * 100).toFixed(0)}% (worst: ${(r.governed.maxVolatility * 100).toFixed(0)}%)`);
|
|
511
|
+
console.log(` Reduction: ${r.impact.avgVolatilityReduction.toFixed(1)}%`);
|
|
512
|
+
if (r.topRules.length > 0) {
|
|
513
|
+
console.log(`\n MOST TRIGGERED RULES`);
|
|
514
|
+
console.log(" " + "-".repeat(50));
|
|
515
|
+
for (const rule of r.topRules) {
|
|
516
|
+
console.log(` ${rule.label} (${rule.triggerCount} times)`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (r.worstCases.length > 0) {
|
|
520
|
+
console.log(`\n WORST-CASE SCENARIOS`);
|
|
521
|
+
console.log(" " + "-".repeat(50));
|
|
522
|
+
for (const wc of r.worstCases) {
|
|
523
|
+
console.log(` Run #${wc.runIndex + 1}: baseline collapse ${(wc.baselineCollapse * 100).toFixed(0)}% → governed ${(wc.governedCollapse * 100).toFixed(0)}% (volatility: ${(wc.volatility * 100).toFixed(0)}%)`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
console.log(`\n TRAJECTORY DISTRIBUTION`);
|
|
527
|
+
console.log(" " + "-".repeat(50));
|
|
528
|
+
const allTrajs = new Set([...Object.keys(r.baseline.trajectories), ...Object.keys(r.governed.trajectories)]);
|
|
529
|
+
for (const traj of allTrajs) {
|
|
530
|
+
const bl = r.baseline.trajectories[traj] ?? 0;
|
|
531
|
+
const gv = r.governed.trajectories[traj] ?? 0;
|
|
532
|
+
console.log(` ${traj.padEnd(14)} baseline: ${bl} governed: ${gv}`);
|
|
533
|
+
}
|
|
534
|
+
console.log(`\n GOVERNANCE ENGINE`);
|
|
535
|
+
console.log(" " + "-".repeat(50));
|
|
536
|
+
console.log(` Engine: ${r.engineStats.engineLoaded ? "@neuroverseos/governance ✓" : "heuristic fallback"}`);
|
|
537
|
+
console.log(` Guard evals: ${r.engineStats.totalGuardEvals}`);
|
|
538
|
+
console.log(` Verdicts: ${r.engineStats.totalVerdicts.allow} allow, ${r.engineStats.totalVerdicts.block} block, ${r.engineStats.totalVerdicts.pause} pause`);
|
|
539
|
+
console.log(` Effectiveness: ${(r.impact.avgEffectiveness * 100).toFixed(0)}%`);
|
|
540
|
+
console.log(` Improvement rate: ${(r.impact.improvementRate * 100).toFixed(0)}% of runs`);
|
|
541
|
+
// Conclusion
|
|
542
|
+
console.log(`\n ${"=".repeat(60)}`);
|
|
543
|
+
const reductionPct = r.baseline.collapseProbability > 0
|
|
544
|
+
? ((r.baseline.collapseProbability - r.governed.collapseProbability) / r.baseline.collapseProbability * 100).toFixed(0)
|
|
545
|
+
: "N/A";
|
|
546
|
+
if (r.baseline.collapseProbability > r.governed.collapseProbability) {
|
|
547
|
+
console.log(` Governance reduced catastrophic failure risk by ${reductionPct}%.`);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
console.log(` System remained stable across all ${r.runsCompleted} scenarios.`);
|
|
551
|
+
}
|
|
552
|
+
console.log(` Tested with nv-sim chaos (${r.runsCompleted} runs, ${(r.durationMs / 1000).toFixed(1)}s)`);
|
|
553
|
+
console.log(` ${"=".repeat(60)}`);
|
|
554
|
+
console.log(`\n Simulate the future. Govern the outcomes.`);
|
|
555
|
+
console.log(` neuroverse-simulations | @neuroverseos\n`);
|
|
556
|
+
}
|
|
557
|
+
// ============================================
|
|
558
|
+
// LOCAL VISUALIZER
|
|
559
|
+
// ============================================
|
|
560
|
+
async function startVisualizer(data, scenarioId) {
|
|
561
|
+
const html = buildVisualizerHtml(data, scenarioId);
|
|
562
|
+
const server = http.createServer((_req, res) => {
|
|
563
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
564
|
+
res.end(html);
|
|
565
|
+
});
|
|
566
|
+
const port = 3456;
|
|
567
|
+
server.listen(port, () => {
|
|
568
|
+
const url = `http://localhost:${port}`;
|
|
569
|
+
console.log(` Visualizer running at ${url}\n`);
|
|
570
|
+
console.log(" Press Ctrl+C to stop.\n");
|
|
571
|
+
// Try to open browser
|
|
572
|
+
const { exec } = require("child_process");
|
|
573
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
574
|
+
exec(`${openCmd} ${url}`, () => { });
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
function buildVisualizerHtml(data, scenarioId) {
|
|
578
|
+
const rounds = data.baseline.swarm.rounds;
|
|
579
|
+
const governedRounds = data.governed.swarm.rounds;
|
|
580
|
+
// Build per-round data for charts
|
|
581
|
+
const roundData = rounds.map((r, i) => {
|
|
582
|
+
const gr = governedRounds[i];
|
|
583
|
+
const blAvg = r.reactions.reduce((s, rx) => s + rx.impact, 0) / r.reactions.length;
|
|
584
|
+
const gvAvg = gr ? gr.reactions.reduce((s, rx) => s + rx.impact, 0) / gr.reactions.length : 0;
|
|
585
|
+
const blVol = Math.max(...r.reactions.map(rx => Math.abs(rx.impact)));
|
|
586
|
+
const gvVol = gr ? Math.max(...gr.reactions.map(rx => Math.abs(rx.impact))) : 0;
|
|
587
|
+
return { round: i, blAvg, gvAvg, blVol, gvVol };
|
|
588
|
+
});
|
|
589
|
+
// Agent data
|
|
590
|
+
const agents = rounds[0]?.reactions.map(r => r.stakeholder_id) ?? [];
|
|
591
|
+
const agentTimelines = agents.map(agentId => ({
|
|
592
|
+
id: agentId,
|
|
593
|
+
baseline: rounds.map(r => {
|
|
594
|
+
const rx = r.reactions.find(a => a.stakeholder_id === agentId);
|
|
595
|
+
return rx ? rx.impact : 0;
|
|
596
|
+
}),
|
|
597
|
+
governed: governedRounds.map(r => {
|
|
598
|
+
const rx = r.reactions.find(a => a.stakeholder_id === agentId);
|
|
599
|
+
return rx ? rx.impact : 0;
|
|
600
|
+
}),
|
|
601
|
+
}));
|
|
602
|
+
// Governance interventions per round
|
|
603
|
+
const dynamics = governedRounds.map(r => r.emergent_dynamics ?? []);
|
|
604
|
+
const jsonData = JSON.stringify({ roundData, agentTimelines, dynamics, scenarioId, data });
|
|
605
|
+
return `<!DOCTYPE html>
|
|
606
|
+
<html lang="en">
|
|
607
|
+
<head>
|
|
608
|
+
<meta charset="UTF-8">
|
|
609
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
610
|
+
<title>NV-SIM Visualizer — ${scenarioId}</title>
|
|
611
|
+
<style>
|
|
612
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
613
|
+
body { font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace; background: #0a0a0a; color: #e0e0e0; padding: 24px; }
|
|
614
|
+
h1 { font-size: 18px; color: #fff; margin-bottom: 4px; }
|
|
615
|
+
.subtitle { color: #888; font-size: 13px; margin-bottom: 24px; }
|
|
616
|
+
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px; }
|
|
617
|
+
.card { background: #141414; border: 1px solid #222; border-radius: 8px; padding: 16px; }
|
|
618
|
+
.card h2 { font-size: 13px; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 12px; }
|
|
619
|
+
.metric { display: flex; justify-content: space-between; padding: 4px 0; font-size: 13px; }
|
|
620
|
+
.metric .label { color: #999; }
|
|
621
|
+
.metric .value { color: #fff; font-weight: 600; }
|
|
622
|
+
.metric .value.good { color: #4ade80; }
|
|
623
|
+
.metric .value.bad { color: #f87171; }
|
|
624
|
+
.metric .value.neutral { color: #fbbf24; }
|
|
625
|
+
canvas { width: 100% !important; height: 200px !important; }
|
|
626
|
+
.full-width { grid-column: 1 / -1; }
|
|
627
|
+
.agent-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px solid #1a1a1a; font-size: 12px; }
|
|
628
|
+
.agent-name { width: 160px; color: #999; flex-shrink: 0; }
|
|
629
|
+
.bar-container { flex: 1; display: flex; gap: 2px; align-items: center; }
|
|
630
|
+
.bar { height: 20px; border-radius: 3px; min-width: 1px; transition: width 0.3s; }
|
|
631
|
+
.bar.baseline { background: #ef4444; opacity: 0.6; }
|
|
632
|
+
.bar.governed { background: #4ade80; opacity: 0.8; }
|
|
633
|
+
.dynamics-list { font-size: 12px; color: #999; }
|
|
634
|
+
.dynamics-list .round-label { color: #fbbf24; font-weight: 600; }
|
|
635
|
+
.dynamics-list p { padding: 3px 0; }
|
|
636
|
+
.tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; margin: 2px; }
|
|
637
|
+
.tag.allow { background: #052e16; color: #4ade80; }
|
|
638
|
+
.tag.block { background: #2d0606; color: #f87171; }
|
|
639
|
+
.tag.pause { background: #2d2006; color: #fbbf24; }
|
|
640
|
+
.footer { text-align: center; color: #444; font-size: 12px; padding: 24px 0; }
|
|
641
|
+
.sparkline { display: flex; align-items: flex-end; gap: 1px; height: 40px; }
|
|
642
|
+
.spark-bar { flex: 1; border-radius: 2px 2px 0 0; min-width: 3px; }
|
|
643
|
+
</style>
|
|
644
|
+
</head>
|
|
645
|
+
<body>
|
|
646
|
+
<h1>NV-SIM Visualizer</h1>
|
|
647
|
+
<div class="subtitle">${scenarioId} — Baseline vs Governed Comparison</div>
|
|
648
|
+
|
|
649
|
+
<div class="grid">
|
|
650
|
+
<div class="card">
|
|
651
|
+
<h2>Simulation A — Baseline (No Governance)</h2>
|
|
652
|
+
<div id="baseline-metrics"></div>
|
|
653
|
+
</div>
|
|
654
|
+
<div class="card">
|
|
655
|
+
<h2>Simulation B — Governed (World Rules)</h2>
|
|
656
|
+
<div id="governed-metrics"></div>
|
|
657
|
+
</div>
|
|
658
|
+
|
|
659
|
+
<div class="card">
|
|
660
|
+
<h2>Impact Timeline — Average Impact Per Round</h2>
|
|
661
|
+
<canvas id="impactChart"></canvas>
|
|
662
|
+
</div>
|
|
663
|
+
<div class="card">
|
|
664
|
+
<h2>Volatility Timeline — Peak Volatility Per Round</h2>
|
|
665
|
+
<canvas id="volatilityChart"></canvas>
|
|
666
|
+
</div>
|
|
667
|
+
|
|
668
|
+
<div class="card full-width">
|
|
669
|
+
<h2>Agent Behavior — Baseline vs Governed (Final Round)</h2>
|
|
670
|
+
<div id="agent-bars"></div>
|
|
671
|
+
</div>
|
|
672
|
+
|
|
673
|
+
<div class="card">
|
|
674
|
+
<h2>Governance Interventions</h2>
|
|
675
|
+
<div id="dynamics" class="dynamics-list"></div>
|
|
676
|
+
</div>
|
|
677
|
+
<div class="card">
|
|
678
|
+
<h2>Governance Engine Stats</h2>
|
|
679
|
+
<div id="engine-stats"></div>
|
|
680
|
+
</div>
|
|
681
|
+
</div>
|
|
682
|
+
|
|
683
|
+
<div class="footer">
|
|
684
|
+
nv-sim visualize — local simulation inspector<br>
|
|
685
|
+
neuroverse-simulations | @neuroverseos
|
|
686
|
+
</div>
|
|
687
|
+
|
|
688
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js"><\/script>
|
|
689
|
+
<script>
|
|
690
|
+
const SIM = ${jsonData};
|
|
691
|
+
|
|
692
|
+
// Metrics
|
|
693
|
+
function renderMetrics(containerId, metrics, trajectory) {
|
|
694
|
+
const el = document.getElementById(containerId);
|
|
695
|
+
const rows = [
|
|
696
|
+
['Trajectory', trajectory.toUpperCase(), trajectory === 'converging' || trajectory === 'stabilizing' ? 'good' : trajectory === 'escalating' ? 'bad' : 'neutral'],
|
|
697
|
+
['Collapse Probability', (metrics.collapseProbability * 100).toFixed(0) + '%', metrics.collapseProbability < 0.2 ? 'good' : metrics.collapseProbability > 0.5 ? 'bad' : 'neutral'],
|
|
698
|
+
['Stability', (metrics.stabilityScore * 100).toFixed(0) + '%', metrics.stabilityScore > 0.7 ? 'good' : metrics.stabilityScore < 0.4 ? 'bad' : 'neutral'],
|
|
699
|
+
['Peak Volatility', (metrics.maxVolatility * 100).toFixed(0) + '%', metrics.maxVolatility < 0.4 ? 'good' : metrics.maxVolatility > 0.7 ? 'bad' : 'neutral'],
|
|
700
|
+
['Coalition Risks', metrics.coalitionRisks, metrics.coalitionRisks === 0 ? 'good' : 'bad'],
|
|
701
|
+
['Polarization Events', metrics.polarizationEvents, metrics.polarizationEvents === 0 ? 'good' : 'neutral'],
|
|
702
|
+
];
|
|
703
|
+
el.innerHTML = rows.map(([label, value, cls]) =>
|
|
704
|
+
'<div class="metric"><span class="label">' + label + '</span><span class="value ' + cls + '">' + value + '</span></div>'
|
|
705
|
+
).join('');
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
renderMetrics('baseline-metrics', SIM.data.baseline.metrics, SIM.data.baseline.swarm.trajectory);
|
|
709
|
+
renderMetrics('governed-metrics', SIM.data.governed.metrics, SIM.data.governed.swarm.trajectory);
|
|
710
|
+
|
|
711
|
+
// Charts
|
|
712
|
+
const labels = SIM.roundData.map(d => 'R' + d.round);
|
|
713
|
+
|
|
714
|
+
if (typeof Chart !== 'undefined') {
|
|
715
|
+
new Chart(document.getElementById('impactChart'), {
|
|
716
|
+
type: 'line',
|
|
717
|
+
data: {
|
|
718
|
+
labels,
|
|
719
|
+
datasets: [
|
|
720
|
+
{ label: 'Baseline', data: SIM.roundData.map(d => d.blAvg), borderColor: '#ef4444', backgroundColor: 'rgba(239,68,68,0.1)', fill: true, tension: 0.3 },
|
|
721
|
+
{ label: 'Governed', data: SIM.roundData.map(d => d.gvAvg), borderColor: '#4ade80', backgroundColor: 'rgba(74,222,128,0.1)', fill: true, tension: 0.3 },
|
|
722
|
+
]
|
|
723
|
+
},
|
|
724
|
+
options: {
|
|
725
|
+
responsive: true,
|
|
726
|
+
plugins: { legend: { labels: { color: '#888', font: { family: 'monospace' } } } },
|
|
727
|
+
scales: {
|
|
728
|
+
x: { ticks: { color: '#666' }, grid: { color: '#1a1a1a' } },
|
|
729
|
+
y: { ticks: { color: '#666' }, grid: { color: '#1a1a1a' }, min: -1, max: 1 }
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
new Chart(document.getElementById('volatilityChart'), {
|
|
735
|
+
type: 'bar',
|
|
736
|
+
data: {
|
|
737
|
+
labels,
|
|
738
|
+
datasets: [
|
|
739
|
+
{ label: 'Baseline', data: SIM.roundData.map(d => d.blVol), backgroundColor: 'rgba(239,68,68,0.6)' },
|
|
740
|
+
{ label: 'Governed', data: SIM.roundData.map(d => d.gvVol), backgroundColor: 'rgba(74,222,128,0.6)' },
|
|
741
|
+
]
|
|
742
|
+
},
|
|
743
|
+
options: {
|
|
744
|
+
responsive: true,
|
|
745
|
+
plugins: { legend: { labels: { color: '#888', font: { family: 'monospace' } } } },
|
|
746
|
+
scales: {
|
|
747
|
+
x: { ticks: { color: '#666' }, grid: { color: '#1a1a1a' } },
|
|
748
|
+
y: { ticks: { color: '#666' }, grid: { color: '#1a1a1a' }, min: 0, max: 1 }
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Agent bars (final round comparison)
|
|
755
|
+
const agentBars = document.getElementById('agent-bars');
|
|
756
|
+
const lastRoundIdx = SIM.agentTimelines[0]?.baseline.length - 1 || 0;
|
|
757
|
+
agentBars.innerHTML = SIM.agentTimelines.map(agent => {
|
|
758
|
+
const blImpact = agent.baseline[lastRoundIdx] || 0;
|
|
759
|
+
const gvImpact = agent.governed[lastRoundIdx] || 0;
|
|
760
|
+
const blWidth = Math.abs(blImpact) * 100;
|
|
761
|
+
const gvWidth = Math.abs(gvImpact) * 100;
|
|
762
|
+
const blColor = blImpact >= 0 ? '#4ade80' : '#ef4444';
|
|
763
|
+
const gvColor = gvImpact >= 0 ? '#4ade80' : '#3b82f6';
|
|
764
|
+
return '<div class="agent-row">' +
|
|
765
|
+
'<span class="agent-name">' + agent.id + '</span>' +
|
|
766
|
+
'<div class="bar-container">' +
|
|
767
|
+
'<div class="bar" style="width:' + blWidth + '%;background:' + blColor + ';opacity:0.4" title="Baseline: ' + blImpact.toFixed(2) + '"></div>' +
|
|
768
|
+
'</div>' +
|
|
769
|
+
'<div class="bar-container">' +
|
|
770
|
+
'<div class="bar" style="width:' + gvWidth + '%;background:' + gvColor + '" title="Governed: ' + gvImpact.toFixed(2) + '"></div>' +
|
|
771
|
+
'</div>' +
|
|
772
|
+
'</div>';
|
|
773
|
+
}).join('');
|
|
774
|
+
|
|
775
|
+
// Dynamics
|
|
776
|
+
const dynamicsEl = document.getElementById('dynamics');
|
|
777
|
+
dynamicsEl.innerHTML = SIM.dynamics.map((roundDyn, i) =>
|
|
778
|
+
roundDyn.length > 0
|
|
779
|
+
? '<p><span class="round-label">Round ' + i + ':</span> ' + roundDyn.join('; ') + '</p>'
|
|
780
|
+
: ''
|
|
781
|
+
).filter(Boolean).join('') || '<p>No interventions triggered.</p>';
|
|
782
|
+
|
|
783
|
+
// Engine stats
|
|
784
|
+
const gs = SIM.data.governanceStats;
|
|
785
|
+
const engineEl = document.getElementById('engine-stats');
|
|
786
|
+
engineEl.innerHTML = [
|
|
787
|
+
['Engine', gs.engineLoaded ? '@neuroverseos/governance' : 'heuristic fallback', gs.engineLoaded ? 'good' : 'neutral'],
|
|
788
|
+
['Guard Evaluations', gs.totalEvaluations, ''],
|
|
789
|
+
['Verdicts', '<span class="tag allow">' + gs.verdicts.allow + ' allow</span><span class="tag block">' + gs.verdicts.block + ' block</span><span class="tag pause">' + gs.verdicts.pause + ' pause</span>', ''],
|
|
790
|
+
['Rules Fired', gs.rulesFired, ''],
|
|
791
|
+
['World Viability', gs.finalViability + (gs.worldCollapsed ? ' (COLLAPSED)' : ''), gs.worldCollapsed ? 'bad' : 'good'],
|
|
792
|
+
['Invariants Checked', gs.invariantsChecked, ''],
|
|
793
|
+
].map(([label, value, cls]) =>
|
|
794
|
+
'<div class="metric"><span class="label">' + label + '</span><span class="value ' + cls + '">' + value + '</span></div>'
|
|
795
|
+
).join('');
|
|
796
|
+
<\/script>
|
|
797
|
+
</body>
|
|
798
|
+
</html>`;
|
|
799
|
+
}
|
|
800
|
+
main().catch((err) => {
|
|
801
|
+
console.error("Error:", err.message);
|
|
802
|
+
process.exit(1);
|
|
803
|
+
});
|