@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.
@@ -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
+ });