@neuroverseos/nv-sim 0.1.4 → 0.1.5
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 +346 -68
- package/dist/adapters/mirofish.js +461 -0
- package/dist/adapters/scienceclaw.js +750 -0
- package/dist/assets/index-B64NuIXu.css +1 -0
- package/dist/assets/index-DbzSnYxr.js +532 -0
- package/dist/assets/{reportEngine-BfteK4MN.js → reportEngine-DKWTrP6-.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 +2787 -360
- package/dist/engine/metrics/science.metrics.js +335 -0
- package/dist/engine/narrativeInjection.js +55 -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/scenarioCapsule.js +56 -0
- package/dist/engine/scenarioComparison.js +463 -0
- package/dist/engine/scenarioLibrary.js +17 -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
|
@@ -15,9 +15,17 @@
|
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.resolveWorld = resolveWorld;
|
|
17
17
|
exports.runWorldComparison = runWorldComparison;
|
|
18
|
+
exports.getAvailableWorlds = getAvailableWorlds;
|
|
19
|
+
exports.explainWorldGovernance = explainWorldGovernance;
|
|
20
|
+
exports.generateComparisonImpact = generateComparisonImpact;
|
|
18
21
|
const scenarioCapsule_1 = require("./scenarioCapsule");
|
|
19
22
|
const governedSimulation_1 = require("./governedSimulation");
|
|
23
|
+
const worldStorage_1 = require("./worldStorage");
|
|
20
24
|
function resolveWorld(presetId) {
|
|
25
|
+
// Check saved worlds first (browser localStorage)
|
|
26
|
+
const saved = resolveSavedWorld(presetId);
|
|
27
|
+
if (saved)
|
|
28
|
+
return saved;
|
|
21
29
|
if (presetId === "trading" || presetId === "flash_crash") {
|
|
22
30
|
return {
|
|
23
31
|
id: "trading-flash-crash",
|
|
@@ -75,6 +83,63 @@ function resolveWorld(presetId) {
|
|
|
75
83
|
],
|
|
76
84
|
};
|
|
77
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Resolve a saved world (from localStorage) into a ResolvedWorld.
|
|
88
|
+
* Saved worlds get merged with the base preset's scenario/stakeholders
|
|
89
|
+
* so they work in the comparison engine.
|
|
90
|
+
*/
|
|
91
|
+
function resolveSavedWorld(id) {
|
|
92
|
+
try {
|
|
93
|
+
const saved = (0, worldStorage_1.loadWorldFromStorage)(id);
|
|
94
|
+
if (!saved)
|
|
95
|
+
return null;
|
|
96
|
+
// Try to get scenario context from the base preset
|
|
97
|
+
let baseScenario = `Simulation with custom world: ${saved.world.thesis}`;
|
|
98
|
+
let baseStakeholders = [
|
|
99
|
+
{ id: "System", disposition: "neutral", priorities: ["stability"] },
|
|
100
|
+
{ id: "Agents", disposition: "unknown", priorities: ["execution"] },
|
|
101
|
+
];
|
|
102
|
+
let baseAssumptions = { severity: "high" };
|
|
103
|
+
let baseConstraints = {};
|
|
104
|
+
try {
|
|
105
|
+
const base = resolveWorld(saved.basePreset);
|
|
106
|
+
baseScenario = base.scenario;
|
|
107
|
+
baseStakeholders = base.stakeholders;
|
|
108
|
+
baseAssumptions = base.assumptions;
|
|
109
|
+
baseConstraints = base.constraints;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Base preset not found — use defaults
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
id: saved.id,
|
|
116
|
+
title: saved.name,
|
|
117
|
+
scenario: baseScenario,
|
|
118
|
+
stakeholders: baseStakeholders,
|
|
119
|
+
assumptions: baseAssumptions,
|
|
120
|
+
constraints: baseConstraints,
|
|
121
|
+
swarm: { enabled: true, rounds: 5, reaction_model: "mixed" },
|
|
122
|
+
depth: "full",
|
|
123
|
+
world: saved.world,
|
|
124
|
+
paths: [
|
|
125
|
+
{
|
|
126
|
+
id: "path_primary",
|
|
127
|
+
label: "Primary Response",
|
|
128
|
+
description: "Most likely course of action",
|
|
129
|
+
projected_outcome: "Moderate disruption with cascading effects",
|
|
130
|
+
probability: 0.6,
|
|
131
|
+
risk: "high",
|
|
132
|
+
tradeoffs: ["Speed vs. thoroughness", "Short-term vs. long-term"],
|
|
133
|
+
benefits_stakeholders: baseStakeholders.filter(s => s.disposition === "supportive").map(s => s.id),
|
|
134
|
+
harms_stakeholders: baseStakeholders.filter(s => s.disposition === "hostile").map(s => s.id),
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
78
143
|
async function runWorldComparison(worldAId, worldBId, options) {
|
|
79
144
|
const worldADef = resolveWorld(worldAId);
|
|
80
145
|
const worldBDef = resolveWorld(worldBId);
|
|
@@ -192,3 +257,102 @@ function buildWorldNarrative(worldA, worldB, metricsA, metricsB, moreStable) {
|
|
|
192
257
|
parts.push("Same agents, different rules — the world shapes the outcome.");
|
|
193
258
|
return parts.join(" ");
|
|
194
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Get all available world presets for the comparison picker.
|
|
262
|
+
*/
|
|
263
|
+
function getAvailableWorlds() {
|
|
264
|
+
const worlds = [
|
|
265
|
+
{
|
|
266
|
+
id: "trading",
|
|
267
|
+
title: "Flash Crash — Algorithmic Cascade",
|
|
268
|
+
thesis: governedSimulation_1.TRADING_DEMO.world.thesis,
|
|
269
|
+
invariantCount: governedSimulation_1.TRADING_DEMO.world.invariants.length,
|
|
270
|
+
gateCount: (governedSimulation_1.TRADING_DEMO.world.gates ?? []).length,
|
|
271
|
+
},
|
|
272
|
+
];
|
|
273
|
+
for (const [key, template] of Object.entries(scenarioCapsule_1.SCENARIO_TEMPLATES)) {
|
|
274
|
+
if (template.world?.inline_definition) {
|
|
275
|
+
worlds.push({
|
|
276
|
+
id: key,
|
|
277
|
+
title: template.title,
|
|
278
|
+
thesis: template.world.inline_definition.thesis,
|
|
279
|
+
invariantCount: template.world.inline_definition.invariants.length,
|
|
280
|
+
gateCount: (template.world.inline_definition.gates ?? []).length,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return worlds;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Generate a plain-English explanation of a world's governance posture.
|
|
288
|
+
* Uses the governance engine's own data — no LLM needed.
|
|
289
|
+
*/
|
|
290
|
+
function explainWorldGovernance(presetId) {
|
|
291
|
+
const world = resolveWorld(presetId);
|
|
292
|
+
const invariants = world.world.invariants;
|
|
293
|
+
const gates = world.world.gates ?? [];
|
|
294
|
+
const enforceableCount = invariants.filter(i => i.enforceable).length;
|
|
295
|
+
const advisoryCount = invariants.length - enforceableCount;
|
|
296
|
+
const invariantSummary = invariants.length === 0
|
|
297
|
+
? "No invariants defined — ungoverned."
|
|
298
|
+
: `${invariants.length} rules (${enforceableCount} enforced, ${advisoryCount} advisory): ${invariants.map(i => i.description).join("; ")}`;
|
|
299
|
+
const criticalGates = gates.filter(g => g.severity === "critical");
|
|
300
|
+
const warningGates = gates.filter(g => g.severity === "warning");
|
|
301
|
+
const gateSummary = gates.length === 0
|
|
302
|
+
? "No viability gates — no collapse detection."
|
|
303
|
+
: `${gates.length} gates (${criticalGates.length} critical, ${warningGates.length} warning): ${gates.map(g => `${g.label} [${g.severity}]`).join(", ")}`;
|
|
304
|
+
const riskProfile = criticalGates.length >= 2 ? "high"
|
|
305
|
+
: criticalGates.length >= 1 || warningGates.length >= 2 ? "moderate"
|
|
306
|
+
: "low";
|
|
307
|
+
return {
|
|
308
|
+
worldId: presetId,
|
|
309
|
+
title: world.title,
|
|
310
|
+
thesis: world.world.thesis,
|
|
311
|
+
invariantSummary,
|
|
312
|
+
gateSummary,
|
|
313
|
+
riskProfile,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Generate a counterfactual impact summary from comparison results.
|
|
318
|
+
* "What would have happened without these rules?"
|
|
319
|
+
*
|
|
320
|
+
* Powered by @neuroverseos/governance engine data.
|
|
321
|
+
*/
|
|
322
|
+
function generateComparisonImpact(result) {
|
|
323
|
+
const { worldA, worldB, delta } = result;
|
|
324
|
+
const winner = delta.moreStable;
|
|
325
|
+
const winnerSide = winner === "A" ? worldA : winner === "B" ? worldB : worldA;
|
|
326
|
+
const loserSide = winner === "A" ? worldB : winner === "B" ? worldA : worldB;
|
|
327
|
+
const interventionDiff = winnerSide.governanceStats.totalEvaluations - loserSide.governanceStats.totalEvaluations;
|
|
328
|
+
const blockDiff = winnerSide.governanceStats.verdicts.block - loserSide.governanceStats.verdicts.block;
|
|
329
|
+
const keyFindings = [];
|
|
330
|
+
if (Math.abs(delta.stabilityDiff) >= 5) {
|
|
331
|
+
keyFindings.push(`${Math.abs(delta.stabilityDiff).toFixed(0)}% stability ${delta.stabilityDiff > 0 ? "advantage for World A" : "advantage for World B"}`);
|
|
332
|
+
}
|
|
333
|
+
if (Math.abs(delta.collapseDiff) >= 5) {
|
|
334
|
+
keyFindings.push(`${Math.abs(delta.collapseDiff).toFixed(0)}% ${delta.collapseDiff > 0 ? "higher" : "lower"} cascade risk in World A`);
|
|
335
|
+
}
|
|
336
|
+
if (blockDiff !== 0) {
|
|
337
|
+
keyFindings.push(`${Math.abs(blockDiff)} more governance interventions in ${blockDiff > 0 ? "World A" : "World B"}`);
|
|
338
|
+
}
|
|
339
|
+
if (Math.abs(delta.volatilityDiff) >= 5) {
|
|
340
|
+
keyFindings.push(`${Math.abs(delta.volatilityDiff).toFixed(0)}% volatility ${delta.volatilityDiff > 0 ? "higher in A" : "higher in B"}`);
|
|
341
|
+
}
|
|
342
|
+
if (keyFindings.length === 0) {
|
|
343
|
+
keyFindings.push("Both worlds produced similar outcomes — rules had minimal differential effect.");
|
|
344
|
+
}
|
|
345
|
+
return {
|
|
346
|
+
winner,
|
|
347
|
+
stabilityDelta: `${delta.stabilityDiff > 0 ? "+" : ""}${delta.stabilityDiff.toFixed(1)}%`,
|
|
348
|
+
cascadeRiskDelta: `${delta.collapseDiff > 0 ? "+" : ""}${delta.collapseDiff.toFixed(1)}%`,
|
|
349
|
+
volatilityDelta: `${delta.volatilityDiff > 0 ? "+" : ""}${delta.volatilityDiff.toFixed(1)}%`,
|
|
350
|
+
totalInterventionsA: worldA.governanceStats.totalEvaluations,
|
|
351
|
+
totalInterventionsB: worldB.governanceStats.totalEvaluations,
|
|
352
|
+
blockedActionsA: worldA.governanceStats.verdicts.block,
|
|
353
|
+
blockedActionsB: worldB.governanceStats.verdicts.block,
|
|
354
|
+
keyFindings,
|
|
355
|
+
verdict: delta.narrative,
|
|
356
|
+
poweredBy: "@neuroverseos/governance — simulateWorld() + evaluateGuard()",
|
|
357
|
+
};
|
|
358
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* World Storage — Decision Memory
|
|
4
|
+
*
|
|
5
|
+
* Save and load custom worlds as JSON. No cloud, no accounts.
|
|
6
|
+
* The world IS the data — portable, shareable, testable.
|
|
7
|
+
*
|
|
8
|
+
* Storage layers:
|
|
9
|
+
* Browser: localStorage (for UI)
|
|
10
|
+
* CLI: JSON files on disk (for dev workflows)
|
|
11
|
+
*
|
|
12
|
+
* A saved world captures:
|
|
13
|
+
* - The base world it was built from (preset ID)
|
|
14
|
+
* - Custom rules the user added
|
|
15
|
+
* - Metadata (name, description, timestamp)
|
|
16
|
+
*
|
|
17
|
+
* This turns transient experiments into reusable decision artifacts.
|
|
18
|
+
*
|
|
19
|
+
* Powered by @neuroverseos/governance world format.
|
|
20
|
+
*/
|
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(o, k2, desc);
|
|
28
|
+
}) : (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
o[k2] = m[k];
|
|
31
|
+
}));
|
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
34
|
+
}) : function(o, v) {
|
|
35
|
+
o["default"] = v;
|
|
36
|
+
});
|
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
38
|
+
var ownKeys = function(o) {
|
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
40
|
+
var ar = [];
|
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
42
|
+
return ar;
|
|
43
|
+
};
|
|
44
|
+
return ownKeys(o);
|
|
45
|
+
};
|
|
46
|
+
return function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.saveWorldToStorage = saveWorldToStorage;
|
|
56
|
+
exports.listWorldsFromStorage = listWorldsFromStorage;
|
|
57
|
+
exports.loadWorldFromStorage = loadWorldFromStorage;
|
|
58
|
+
exports.deleteWorldFromStorage = deleteWorldFromStorage;
|
|
59
|
+
exports.exportWorldToJSON = exportWorldToJSON;
|
|
60
|
+
exports.importWorldFromJSON = importWorldFromJSON;
|
|
61
|
+
exports.createSavedWorld = createSavedWorld;
|
|
62
|
+
exports.forkWorld = forkWorld;
|
|
63
|
+
exports.saveWorldToFile = saveWorldToFile;
|
|
64
|
+
exports.loadWorldFromFile = loadWorldFromFile;
|
|
65
|
+
// ============================================
|
|
66
|
+
// BROWSER STORAGE (localStorage)
|
|
67
|
+
// ============================================
|
|
68
|
+
const STORAGE_KEY = "nv-sim-saved-worlds";
|
|
69
|
+
/**
|
|
70
|
+
* Runtime registry for worlds loaded from files (CLI context).
|
|
71
|
+
* Falls back to this when localStorage is unavailable.
|
|
72
|
+
*/
|
|
73
|
+
const runtimeRegistry = [];
|
|
74
|
+
function hasLocalStorage() {
|
|
75
|
+
try {
|
|
76
|
+
return typeof localStorage !== "undefined" && localStorage !== null;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Save a world to browser localStorage (or runtime registry in CLI).
|
|
84
|
+
*/
|
|
85
|
+
function saveWorldToStorage(world) {
|
|
86
|
+
if (!hasLocalStorage()) {
|
|
87
|
+
const idx = runtimeRegistry.findIndex(w => w.id === world.id);
|
|
88
|
+
if (idx >= 0)
|
|
89
|
+
runtimeRegistry[idx] = world;
|
|
90
|
+
else
|
|
91
|
+
runtimeRegistry.push(world);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const existing = listWorldsFromStorage();
|
|
95
|
+
const idx = existing.findIndex(w => w.id === world.id);
|
|
96
|
+
if (idx >= 0) {
|
|
97
|
+
existing[idx] = { ...world, modifiedAt: new Date().toISOString() };
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
existing.push(world);
|
|
101
|
+
}
|
|
102
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(existing));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* List all saved worlds from browser localStorage (or runtime registry).
|
|
106
|
+
*/
|
|
107
|
+
function listWorldsFromStorage() {
|
|
108
|
+
if (!hasLocalStorage()) {
|
|
109
|
+
return [...runtimeRegistry];
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
113
|
+
if (!raw)
|
|
114
|
+
return [];
|
|
115
|
+
return JSON.parse(raw);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Load a specific saved world by ID from localStorage (or runtime registry).
|
|
123
|
+
*/
|
|
124
|
+
function loadWorldFromStorage(id) {
|
|
125
|
+
const worlds = listWorldsFromStorage();
|
|
126
|
+
return worlds.find(w => w.id === id) ?? null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Delete a saved world from localStorage (or runtime registry).
|
|
130
|
+
*/
|
|
131
|
+
function deleteWorldFromStorage(id) {
|
|
132
|
+
if (!hasLocalStorage()) {
|
|
133
|
+
const idx = runtimeRegistry.findIndex(w => w.id === id);
|
|
134
|
+
if (idx < 0)
|
|
135
|
+
return false;
|
|
136
|
+
runtimeRegistry.splice(idx, 1);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
const worlds = listWorldsFromStorage();
|
|
140
|
+
const filtered = worlds.filter(w => w.id !== id);
|
|
141
|
+
if (filtered.length === worlds.length)
|
|
142
|
+
return false;
|
|
143
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(filtered));
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
// ============================================
|
|
147
|
+
// JSON EXPORT / IMPORT
|
|
148
|
+
// ============================================
|
|
149
|
+
/**
|
|
150
|
+
* Export a saved world to a JSON string (for file download or CLI).
|
|
151
|
+
*/
|
|
152
|
+
function exportWorldToJSON(world) {
|
|
153
|
+
return JSON.stringify(world, null, 2);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Import a saved world from a JSON string.
|
|
157
|
+
* Validates the structure before returning.
|
|
158
|
+
*/
|
|
159
|
+
function importWorldFromJSON(json) {
|
|
160
|
+
const parsed = JSON.parse(json);
|
|
161
|
+
if (!parsed.version || !parsed.id || !parsed.name || !parsed.world) {
|
|
162
|
+
throw new Error("Invalid world format: missing required fields (version, id, name, world)");
|
|
163
|
+
}
|
|
164
|
+
if (!parsed.world.thesis || !Array.isArray(parsed.world.invariants)) {
|
|
165
|
+
throw new Error("Invalid world definition: missing thesis or invariants");
|
|
166
|
+
}
|
|
167
|
+
return parsed;
|
|
168
|
+
}
|
|
169
|
+
// ============================================
|
|
170
|
+
// WORLD CREATION HELPERS
|
|
171
|
+
// ============================================
|
|
172
|
+
/**
|
|
173
|
+
* Create a new SavedWorld from a world definition.
|
|
174
|
+
*/
|
|
175
|
+
function createSavedWorld(name, world, basePreset, options) {
|
|
176
|
+
const id = `world_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
177
|
+
const now = new Date().toISOString();
|
|
178
|
+
return {
|
|
179
|
+
version: "1.0",
|
|
180
|
+
id,
|
|
181
|
+
name,
|
|
182
|
+
description: options?.description,
|
|
183
|
+
basePreset,
|
|
184
|
+
world,
|
|
185
|
+
createdAt: now,
|
|
186
|
+
modifiedAt: now,
|
|
187
|
+
tags: options?.tags,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Fork an existing world with modifications.
|
|
192
|
+
*/
|
|
193
|
+
function forkWorld(source, name, modifications) {
|
|
194
|
+
const invariants = [
|
|
195
|
+
...source.world.invariants.filter(i => !modifications.removeInvariantIds?.includes(i.id)),
|
|
196
|
+
...(modifications.addInvariants ?? []),
|
|
197
|
+
];
|
|
198
|
+
const gates = [
|
|
199
|
+
...(source.world.gates ?? []).filter(g => !modifications.removeGateIds?.includes(g.id)),
|
|
200
|
+
...(modifications.addGates ?? []),
|
|
201
|
+
];
|
|
202
|
+
const world = {
|
|
203
|
+
...source.world,
|
|
204
|
+
thesis: modifications.thesis ?? source.world.thesis,
|
|
205
|
+
invariants,
|
|
206
|
+
gates,
|
|
207
|
+
};
|
|
208
|
+
return createSavedWorld(name, world, source.id, {
|
|
209
|
+
description: `Forked from "${source.name}"`,
|
|
210
|
+
tags: [...(source.tags ?? []), "forked"],
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// ============================================
|
|
214
|
+
// FILE I/O (Node.js only — for CLI)
|
|
215
|
+
// ============================================
|
|
216
|
+
/**
|
|
217
|
+
* Save a world to a JSON file on disk.
|
|
218
|
+
* Only works in Node.js (CLI context).
|
|
219
|
+
*/
|
|
220
|
+
async function saveWorldToFile(world, filePath) {
|
|
221
|
+
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
222
|
+
fs.writeFileSync(filePath, exportWorldToJSON(world), "utf-8");
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Load a world from a JSON file on disk.
|
|
226
|
+
* Only works in Node.js (CLI context).
|
|
227
|
+
*/
|
|
228
|
+
async function loadWorldFromFile(filePath) {
|
|
229
|
+
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
230
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
231
|
+
return importWorldFromJSON(content);
|
|
232
|
+
}
|
package/dist/index.html
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
<meta name="twitter:title" content="NV-SIM — NeuroVerse Simulation">
|
|
15
15
|
<meta name="twitter:description" content="Governed agent simulation — compare emergent outcomes with and without governance constraints.">
|
|
16
16
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
|
17
|
-
<script type="module" crossorigin src="/assets/index-
|
|
18
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
17
|
+
<script type="module" crossorigin src="/assets/index-DbzSnYxr.js"></script>
|
|
18
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B64NuIXu.css">
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
21
21
|
<div id="root"></div>
|