@neuroverseos/nv-sim 0.1.2 → 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 +562 -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/mirotir-logo-DUexumBH.svg +185 -0
- package/dist/assets/reportEngine-DKWTrP6-.js +1 -0
- package/dist/components/ConstraintsPanel.js +11 -0
- package/dist/components/StakeholderBuilder.js +32 -0
- package/dist/components/ui/badge.js +24 -0
- package/dist/components/ui/button.js +70 -0
- package/dist/components/ui/card.js +57 -0
- package/dist/components/ui/input.js +44 -0
- package/dist/components/ui/label.js +45 -0
- package/dist/components/ui/select.js +70 -0
- package/dist/engine/aiProvider.js +681 -0
- package/dist/engine/auditTrace.js +352 -0
- package/dist/engine/behavioralAnalysis.js +605 -0
- package/dist/engine/cli.js +1408 -299
- package/dist/engine/dynamicsGovernance.js +588 -0
- package/dist/engine/fullGovernedLoop.js +367 -0
- package/dist/engine/governance.js +8 -3
- package/dist/engine/governedSimulation.js +114 -17
- package/dist/engine/index.js +56 -1
- package/dist/engine/liveAdapter.js +342 -0
- package/dist/engine/liveVisualizer.js +4284 -0
- package/dist/engine/metrics/science.metrics.js +335 -0
- package/dist/engine/narrativeInjection.js +360 -0
- package/dist/engine/policyEnforcement.js +1611 -0
- package/dist/engine/policyEngine.js +799 -0
- package/dist/engine/primeRadiant.js +540 -0
- package/dist/engine/reasoningEngine.js +57 -3
- package/dist/engine/reportEngine.js +97 -0
- package/dist/engine/scenarioCapsule.js +56 -0
- package/dist/engine/scenarioComparison.js +463 -0
- package/dist/engine/scenarioLibrary.js +248 -0
- package/dist/engine/swarmSimulation.js +54 -1
- package/dist/engine/worldComparison.js +358 -0
- package/dist/engine/worldStorage.js +232 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.html +23 -0
- package/dist/lib/reasoningEngine.js +290 -0
- package/dist/lib/simulationAdapter.js +686 -0
- package/dist/lib/swarmParser.js +291 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/utils.js +8 -0
- package/dist/placeholder.svg +1 -0
- package/dist/robots.txt +14 -0
- package/dist/runtime/govern.js +473 -0
- package/dist/runtime/index.js +75 -0
- package/dist/runtime/types.js +11 -0
- package/package.json +17 -12
- package/variants/.gitkeep +0 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Science Metrics Module — Evaluation Metrics for Autonomous Discovery
|
|
4
|
+
*
|
|
5
|
+
* "ScienceClaw explores possibility. NeuroVerse enforces reality."
|
|
6
|
+
*
|
|
7
|
+
* These metrics evaluate the quality of governed scientific discovery.
|
|
8
|
+
* They plug directly into scenarioComparison.ts as EvaluationMetric[].
|
|
9
|
+
*
|
|
10
|
+
* The five core science metrics:
|
|
11
|
+
* 1. Convergence Quality — are we converging on truth, or on noise?
|
|
12
|
+
* 2. Reproducibility Score — can findings be independently validated?
|
|
13
|
+
* 3. Provenance Depth — how deep is the evidence chain?
|
|
14
|
+
* 4. Compute Efficiency — are we burning cycles on dead ends?
|
|
15
|
+
* 5. False Positive Rate — how much garbage survives the pipeline?
|
|
16
|
+
*
|
|
17
|
+
* Science-specific system state extends the base SystemState with:
|
|
18
|
+
* - artifact_density: how many artifacts per round
|
|
19
|
+
* - validation_gap: how far behind validation lags discovery
|
|
20
|
+
* - citation_circularity: self-referencing chains
|
|
21
|
+
* - hypothesis_diversity: breadth of active investigation
|
|
22
|
+
* - convergence_velocity: how fast hypotheses collapse to consensus
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.SCIENCE_INITIAL_STATE = exports.SCIENCE_POLICY_TEXT = exports.SCIENCE_METRICS = void 0;
|
|
26
|
+
exports.interpretScienceState = interpretScienceState;
|
|
27
|
+
// ============================================
|
|
28
|
+
// METRIC EXTRACTORS
|
|
29
|
+
// ============================================
|
|
30
|
+
/**
|
|
31
|
+
* Extract convergence quality from simulation results.
|
|
32
|
+
*
|
|
33
|
+
* Good convergence:
|
|
34
|
+
* - Low polarization (hypotheses are consolidating, not fragmenting)
|
|
35
|
+
* - Moderate velocity (not too fast = shallow, not too slow = stuck)
|
|
36
|
+
* - High trust (findings are reproducible)
|
|
37
|
+
*
|
|
38
|
+
* Bad convergence:
|
|
39
|
+
* - High polarization (field is fragmenting)
|
|
40
|
+
* - Very high velocity (premature consensus without evidence)
|
|
41
|
+
* - Low trust (findings can't be reproduced)
|
|
42
|
+
*/
|
|
43
|
+
function extractConvergenceQuality(result) {
|
|
44
|
+
const state = result.finalState;
|
|
45
|
+
// Low polarization → hypotheses consolidating (good)
|
|
46
|
+
const consolidation = 1 - state.polarization;
|
|
47
|
+
// Moderate velocity is ideal — too fast is shallow, too slow is stuck
|
|
48
|
+
// Sweet spot: 0.3-0.6
|
|
49
|
+
const velocityScore = state.informationVelocity >= 0.3 && state.informationVelocity <= 0.6
|
|
50
|
+
? 1.0
|
|
51
|
+
: state.informationVelocity < 0.3
|
|
52
|
+
? state.informationVelocity / 0.3 // too slow
|
|
53
|
+
: 1 - (state.informationVelocity - 0.6) / 0.4; // too fast
|
|
54
|
+
// High trust → reproducible findings
|
|
55
|
+
const reproducibility = state.trust;
|
|
56
|
+
// Low cascade risk → findings aren't propagating unchecked
|
|
57
|
+
const integrityScore = 1 - state.cascadeRisk;
|
|
58
|
+
return (consolidation * 0.3 +
|
|
59
|
+
velocityScore * 0.25 +
|
|
60
|
+
reproducibility * 0.25 +
|
|
61
|
+
integrityScore * 0.2);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Extract reproducibility score.
|
|
65
|
+
*
|
|
66
|
+
* In science mode:
|
|
67
|
+
* trust = can we reproduce findings?
|
|
68
|
+
* low outrage = no publication pressure forcing premature claims
|
|
69
|
+
* low cascade risk = bad results aren't propagating through citations
|
|
70
|
+
*/
|
|
71
|
+
function extractReproducibilityScore(result) {
|
|
72
|
+
const state = result.finalState;
|
|
73
|
+
// Trust is the primary indicator of reproducibility
|
|
74
|
+
const trustScore = state.trust;
|
|
75
|
+
// Low publication pressure (outrage = rush to publish)
|
|
76
|
+
const pressureScore = 1 - state.outrage;
|
|
77
|
+
// Low cascade risk = validated results, not propagated noise
|
|
78
|
+
const validationScore = 1 - state.cascadeRisk;
|
|
79
|
+
// Governance effectiveness indicates active quality control
|
|
80
|
+
const govEffectiveness = result.dynamicsGovernance.totalInterventions > 0
|
|
81
|
+
? Math.min(1, result.dynamicsGovernance.trustBoosts / Math.max(1, result.dynamicsGovernance.totalRounds) + 0.3)
|
|
82
|
+
: 0.3;
|
|
83
|
+
return (trustScore * 0.35 +
|
|
84
|
+
pressureScore * 0.2 +
|
|
85
|
+
validationScore * 0.25 +
|
|
86
|
+
govEffectiveness * 0.2);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Extract provenance depth score.
|
|
90
|
+
*
|
|
91
|
+
* Deeper provenance chains = better evidence foundations.
|
|
92
|
+
* In the simulation, this maps to:
|
|
93
|
+
* - contagionDepth (how deep content propagates = evidence chain depth)
|
|
94
|
+
* - Low amplification (not relying on popularity over evidence)
|
|
95
|
+
* - Multiple rounds of stable findings (temporal depth)
|
|
96
|
+
*/
|
|
97
|
+
function extractProvenanceDepth(result) {
|
|
98
|
+
const state = result.finalState;
|
|
99
|
+
const timeline = result.stateTimeline;
|
|
100
|
+
// Contagion depth as proxy for evidence chain depth (normalized)
|
|
101
|
+
// In science: deeper propagation means findings pass through more validation layers
|
|
102
|
+
const depthScore = Math.min(1, state.contagionDepth / 5);
|
|
103
|
+
// Low amplification = findings stand on evidence, not popularity
|
|
104
|
+
const evidenceVsPopularity = Math.max(0, 1 - (state.amplification - 0.5));
|
|
105
|
+
// Temporal depth: how many rounds did findings persist stably?
|
|
106
|
+
let stableRounds = 0;
|
|
107
|
+
for (let i = 1; i < timeline.length; i++) {
|
|
108
|
+
const delta = Math.abs(timeline[i].trust - timeline[i - 1].trust);
|
|
109
|
+
if (delta < 0.05)
|
|
110
|
+
stableRounds++;
|
|
111
|
+
}
|
|
112
|
+
const temporalDepth = timeline.length > 1
|
|
113
|
+
? stableRounds / (timeline.length - 1)
|
|
114
|
+
: 0.5;
|
|
115
|
+
return (depthScore * 0.35 +
|
|
116
|
+
evidenceVsPopularity * 0.3 +
|
|
117
|
+
temporalDepth * 0.35);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Extract compute efficiency.
|
|
121
|
+
*
|
|
122
|
+
* Efficient discovery:
|
|
123
|
+
* - Not too many governance interventions (focused paths)
|
|
124
|
+
* - Good block rate (filtering noise early, not late)
|
|
125
|
+
* - Low variance in impact (agents working in coordination, not chaos)
|
|
126
|
+
*/
|
|
127
|
+
function extractComputeEfficiency(result) {
|
|
128
|
+
const ag = result.actionGovernance;
|
|
129
|
+
const dg = result.dynamicsGovernance;
|
|
130
|
+
// Block rate: some blocking is efficient (filtering noise), too much is wasteful
|
|
131
|
+
const blockRate = ag.totalEvaluations > 0 ? ag.blocked / ag.totalEvaluations : 0;
|
|
132
|
+
let filterEfficiency;
|
|
133
|
+
if (blockRate < 0.1)
|
|
134
|
+
filterEfficiency = 0.4; // too permissive — noise gets through
|
|
135
|
+
else if (blockRate < 0.3)
|
|
136
|
+
filterEfficiency = 0.9; // sweet spot
|
|
137
|
+
else if (blockRate < 0.5)
|
|
138
|
+
filterEfficiency = 0.7; // slightly aggressive
|
|
139
|
+
else
|
|
140
|
+
filterEfficiency = 0.3; // over-filtering — blocking productive work
|
|
141
|
+
// Intervention density: interventions per round
|
|
142
|
+
// Some interventions = course correcting (good)
|
|
143
|
+
// Too many = system is fundamentally unstable
|
|
144
|
+
const interventionDensity = dg.totalRounds > 0
|
|
145
|
+
? dg.totalInterventions / dg.totalRounds
|
|
146
|
+
: 0;
|
|
147
|
+
const interventionScore = interventionDensity <= 2
|
|
148
|
+
? 0.8 + interventionDensity * 0.1
|
|
149
|
+
: Math.max(0.2, 1 - (interventionDensity - 2) * 0.15);
|
|
150
|
+
// Impact consistency: low variance in final reactions = coordinated work
|
|
151
|
+
const rounds = result.rounds;
|
|
152
|
+
if (rounds.length === 0)
|
|
153
|
+
return 0.5;
|
|
154
|
+
const lastRound = rounds[rounds.length - 1];
|
|
155
|
+
const impacts = lastRound.finalReactions.map(r => r.impact);
|
|
156
|
+
const mean = impacts.reduce((s, i) => s + i, 0) / impacts.length;
|
|
157
|
+
const variance = impacts.reduce((s, i) => s + (i - mean) ** 2, 0) / impacts.length;
|
|
158
|
+
const coordinationScore = Math.max(0, 1 - variance * 3);
|
|
159
|
+
return (filterEfficiency * 0.35 +
|
|
160
|
+
interventionScore * 0.3 +
|
|
161
|
+
coordinationScore * 0.35);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Extract false positive rate (inverted — higher returned value = fewer false positives = better).
|
|
165
|
+
*
|
|
166
|
+
* False positives in science:
|
|
167
|
+
* - High cascade risk = bad results propagating through citation chains
|
|
168
|
+
* - High amplification = popular but wrong findings being boosted
|
|
169
|
+
* - Low trust = findings can't be reproduced
|
|
170
|
+
* - High outrage = publication pressure creating rushed claims
|
|
171
|
+
*/
|
|
172
|
+
function extractFalsePositiveRate(result) {
|
|
173
|
+
const state = result.finalState;
|
|
174
|
+
const dg = result.dynamicsGovernance;
|
|
175
|
+
// Low cascade risk = bad results caught before spreading
|
|
176
|
+
const cascadeControl = 1 - state.cascadeRisk;
|
|
177
|
+
// Low amplification = not amplifying unvalidated findings
|
|
178
|
+
const amplificationControl = Math.max(0, 1 - (state.amplification - 0.5) * 2);
|
|
179
|
+
// High trust = reproducible findings
|
|
180
|
+
const trustScore = state.trust;
|
|
181
|
+
// Cascade breakers fired = system caught and stopped false positives
|
|
182
|
+
const breakerBonus = dg.cascadeBreakers > 0 ? 0.1 : 0;
|
|
183
|
+
// Low publication pressure
|
|
184
|
+
const pressureScore = 1 - state.outrage;
|
|
185
|
+
const raw = (cascadeControl * 0.3 +
|
|
186
|
+
amplificationControl * 0.2 +
|
|
187
|
+
trustScore * 0.25 +
|
|
188
|
+
pressureScore * 0.15 +
|
|
189
|
+
breakerBonus);
|
|
190
|
+
return Math.min(1, raw + 0.1); // slight bias toward positive — governance is working
|
|
191
|
+
}
|
|
192
|
+
// ============================================
|
|
193
|
+
// EXPORTED SCIENCE METRICS
|
|
194
|
+
// ============================================
|
|
195
|
+
/**
|
|
196
|
+
* Science evaluation metrics — plug directly into scenarioComparison.ts.
|
|
197
|
+
*
|
|
198
|
+
* Usage:
|
|
199
|
+
* import { SCIENCE_METRICS } from "./metrics/science.metrics"
|
|
200
|
+
* const result = await runScenarioMatrix({
|
|
201
|
+
* ...config,
|
|
202
|
+
* evaluationMetrics: SCIENCE_METRICS,
|
|
203
|
+
* })
|
|
204
|
+
*/
|
|
205
|
+
exports.SCIENCE_METRICS = [
|
|
206
|
+
{
|
|
207
|
+
id: "convergence_quality",
|
|
208
|
+
label: "Convergence Quality",
|
|
209
|
+
weight: 0.25,
|
|
210
|
+
extractor: extractConvergenceQuality,
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: "reproducibility",
|
|
214
|
+
label: "Reproducibility Score",
|
|
215
|
+
weight: 0.25,
|
|
216
|
+
extractor: extractReproducibilityScore,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "provenance_depth",
|
|
220
|
+
label: "Provenance Depth",
|
|
221
|
+
weight: 0.20,
|
|
222
|
+
extractor: extractProvenanceDepth,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: "compute_efficiency",
|
|
226
|
+
label: "Compute Efficiency",
|
|
227
|
+
weight: 0.15,
|
|
228
|
+
extractor: extractComputeEfficiency,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: "false_positive_control",
|
|
232
|
+
label: "False Positive Control",
|
|
233
|
+
weight: 0.15,
|
|
234
|
+
extractor: extractFalsePositiveRate,
|
|
235
|
+
},
|
|
236
|
+
];
|
|
237
|
+
// ============================================
|
|
238
|
+
// SCIENCE-SPECIFIC DYNAMICS RULES (policy text)
|
|
239
|
+
// ============================================
|
|
240
|
+
/**
|
|
241
|
+
* Default policy text for scientific discovery governance.
|
|
242
|
+
* These map to dynamics rules in governDynamics():
|
|
243
|
+
* - propagation rules → hypothesis spread control
|
|
244
|
+
* - amplification rules → citation amplification dampening
|
|
245
|
+
* - cascade rules → false positive cascade breaking
|
|
246
|
+
* - trust rules → reproducibility enforcement
|
|
247
|
+
* - feedback rules → publication pressure dampening
|
|
248
|
+
*/
|
|
249
|
+
exports.SCIENCE_POLICY_TEXT = [
|
|
250
|
+
// Propagation control — limit hypothesis spread without evidence
|
|
251
|
+
"Limit propagation of causal claims that lack multi-source evidence",
|
|
252
|
+
"Contain unvalidated hypotheses within originating research group",
|
|
253
|
+
"Quarantine findings that have not passed independent reproduction",
|
|
254
|
+
// Amplification dampening — prevent citation echo chambers
|
|
255
|
+
"Dampen amplification of self-citation loops exceeding 3 generations",
|
|
256
|
+
"Limit influence of popular findings that lack independent validation",
|
|
257
|
+
"Reduce visibility of papers with high citation velocity but low reproduction rate",
|
|
258
|
+
// Cascade breaking — stop false positives from compounding
|
|
259
|
+
"Circuit break when cascade of dependent findings exceeds validation capacity",
|
|
260
|
+
"Halt chain reaction of derivative conclusions from unvalidated premises",
|
|
261
|
+
"Break feedback loops between competing research groups amplifying each other",
|
|
262
|
+
// Trust / reproducibility enforcement
|
|
263
|
+
"Boost visibility of independently reproduced findings",
|
|
264
|
+
"Require transparency in methodology for all published artifacts",
|
|
265
|
+
"Prioritize credibility of findings with transparent provenance chains",
|
|
266
|
+
// Publication pressure dampening
|
|
267
|
+
"Dampen rush-to-publish behavior during high-competition periods",
|
|
268
|
+
"Cool down reactive publication patterns when controversy index rises",
|
|
269
|
+
"Stabilize publication rate during peer review bottlenecks",
|
|
270
|
+
// Compute governance
|
|
271
|
+
"Pause redundant investigation paths when convergence detected",
|
|
272
|
+
"Halt compute-intensive explorations that show no evidence of progress",
|
|
273
|
+
].join("\n");
|
|
274
|
+
// ============================================
|
|
275
|
+
// SCIENCE STATE INITIALIZATION
|
|
276
|
+
// ============================================
|
|
277
|
+
/**
|
|
278
|
+
* Initial system state tuned for scientific discovery.
|
|
279
|
+
*
|
|
280
|
+
* Differences from behavior simulation:
|
|
281
|
+
* - Higher trust baseline (science starts from credibility)
|
|
282
|
+
* - Lower outrage (publication pressure starts manageable)
|
|
283
|
+
* - Lower amplification (citations start organic)
|
|
284
|
+
* - Moderate velocity (discovery is active but not frantic)
|
|
285
|
+
*/
|
|
286
|
+
exports.SCIENCE_INITIAL_STATE = {
|
|
287
|
+
polarization: 0.15, // Some hypothesis diversity (healthy)
|
|
288
|
+
outrage: 0.05, // Low publication pressure initially
|
|
289
|
+
trust: 0.8, // Science starts with high credibility
|
|
290
|
+
informationVelocity: 0.4, // Active discovery pace
|
|
291
|
+
amplification: 0.6, // Organic citation patterns
|
|
292
|
+
cascadeRisk: 0.05, // Low false positive risk initially
|
|
293
|
+
contagionDepth: 3, // Moderate evidence chain depth
|
|
294
|
+
coolingPeriod: 0,
|
|
295
|
+
};
|
|
296
|
+
// ============================================
|
|
297
|
+
// SCIENCE STATE INTERPRETER
|
|
298
|
+
// ============================================
|
|
299
|
+
/**
|
|
300
|
+
* Interpret base SystemState through a science lens.
|
|
301
|
+
* Maps generic governance metrics to science-specific language.
|
|
302
|
+
*/
|
|
303
|
+
function interpretScienceState(state) {
|
|
304
|
+
const fragLevel = state.polarization > 0.6 ? "critical" : state.polarization > 0.3 ? "elevated" : "healthy";
|
|
305
|
+
const pressureLevel = state.outrage > 0.5 ? "dangerous" : state.outrage > 0.2 ? "elevated" : "normal";
|
|
306
|
+
const reproLevel = state.trust > 0.7 ? "high" : state.trust > 0.4 ? "moderate" : "crisis";
|
|
307
|
+
const citationLevel = state.amplification > 1.5 ? "echo chamber" : state.amplification > 0.8 ? "amplified" : "organic";
|
|
308
|
+
const fpRisk = state.cascadeRisk > 0.5 ? "high" : state.cascadeRisk > 0.2 ? "moderate" : "controlled";
|
|
309
|
+
const velocity = state.informationVelocity > 0.7 ? "frantic" : state.informationVelocity > 0.3 ? "active" : "stalled";
|
|
310
|
+
let overall;
|
|
311
|
+
if (state.trust > 0.6 && state.cascadeRisk < 0.3 && state.polarization < 0.4) {
|
|
312
|
+
overall = "Healthy discovery — findings are converging with evidence support";
|
|
313
|
+
}
|
|
314
|
+
else if (state.cascadeRisk > 0.6) {
|
|
315
|
+
overall = "False positive cascade risk — unvalidated findings propagating through citations";
|
|
316
|
+
}
|
|
317
|
+
else if (state.polarization > 0.6) {
|
|
318
|
+
overall = "Field fragmentation — competing hypotheses without resolution mechanism";
|
|
319
|
+
}
|
|
320
|
+
else if (state.trust < 0.3) {
|
|
321
|
+
overall = "Reproducibility crisis — institutional credibility collapsing";
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
overall = "Active investigation — governance maintaining discovery quality";
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
hypothesisFragmentation: `${fragLevel} (${(state.polarization * 100).toFixed(0)}%)`,
|
|
328
|
+
publicationPressure: `${pressureLevel} (${(state.outrage * 100).toFixed(0)}%)`,
|
|
329
|
+
reproducibilityConfidence: `${reproLevel} (${(state.trust * 100).toFixed(0)}%)`,
|
|
330
|
+
citationHealth: `${citationLevel} (${state.amplification.toFixed(2)}x)`,
|
|
331
|
+
falsePositiveRisk: `${fpRisk} (${(state.cascadeRisk * 100).toFixed(0)}%)`,
|
|
332
|
+
discoveryVelocity: `${velocity} (${(state.informationVelocity * 100).toFixed(0)}%)`,
|
|
333
|
+
overallAssessment: overall,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Narrative Injection — Information Shocks for Agent Simulations
|
|
4
|
+
*
|
|
5
|
+
* The third knob: agents + world rules + narrative events.
|
|
6
|
+
*
|
|
7
|
+
* Narrative injection models how information shocks propagate through
|
|
8
|
+
* agent networks. Instead of just changing rules, you inject events
|
|
9
|
+
* that shift beliefs, trigger reactions, and reorganize clusters.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* nv-sim compare --inject tanker_explosion@5
|
|
13
|
+
* nv-sim worlds trading strait_of_hormuz --inject rate_cut@3,sanctions@6
|
|
14
|
+
* nv-sim visualize --inject "China begins military exercises"@4
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.NARRATIVE_PRESETS = void 0;
|
|
18
|
+
exports.injectNarrative = injectNarrative;
|
|
19
|
+
exports.parseInjectArgs = parseInjectArgs;
|
|
20
|
+
exports.getEventsForRound = getEventsForRound;
|
|
21
|
+
// ============================================
|
|
22
|
+
// SEVERITY → MAGNITUDE MAPPING
|
|
23
|
+
// ============================================
|
|
24
|
+
const SEVERITY_MAGNITUDE = {
|
|
25
|
+
minor: 0.1,
|
|
26
|
+
moderate: 0.25,
|
|
27
|
+
major: 0.45,
|
|
28
|
+
extreme: 0.7,
|
|
29
|
+
};
|
|
30
|
+
const PROPAGATION_MULTIPLIER = {
|
|
31
|
+
slow: 0.6,
|
|
32
|
+
normal: 1.0,
|
|
33
|
+
viral: 1.4,
|
|
34
|
+
};
|
|
35
|
+
// ============================================
|
|
36
|
+
// NARRATIVE INJECTION ENGINE
|
|
37
|
+
// ============================================
|
|
38
|
+
/**
|
|
39
|
+
* Apply a narrative event to a set of agent reactions.
|
|
40
|
+
*
|
|
41
|
+
* This models how an information shock propagates through the network:
|
|
42
|
+
* - Targeted agents get the full impact
|
|
43
|
+
* - Related agents get partial impact (network effect)
|
|
44
|
+
* - Unrelated agents get minimal impact (background noise)
|
|
45
|
+
* - Confidence shifts based on event clarity
|
|
46
|
+
*/
|
|
47
|
+
function injectNarrative(reactions, event, stakeholders) {
|
|
48
|
+
const magnitude = SEVERITY_MAGNITUDE[event.severity];
|
|
49
|
+
const propagation = PROPAGATION_MULTIPLIER[event.propagation];
|
|
50
|
+
const direction = event.direction === "positive" ? 1 : event.direction === "negative" ? -1 : 0;
|
|
51
|
+
const agentShifts = [];
|
|
52
|
+
const systemEffects = [];
|
|
53
|
+
systemEffects.push(`EVENT INJECTED: "${event.headline}"`);
|
|
54
|
+
systemEffects.push(`Severity: ${event.severity} | Propagation: ${event.propagation}`);
|
|
55
|
+
for (const reaction of reactions) {
|
|
56
|
+
const stakeholder = stakeholders.find(s => s.id === reaction.stakeholder_id);
|
|
57
|
+
const isTargeted = event.targets.some(t => reaction.stakeholder_id.toLowerCase().includes(t.toLowerCase()) ||
|
|
58
|
+
t.toLowerCase().includes(reaction.stakeholder_id.toLowerCase()));
|
|
59
|
+
// Determine how much this agent is affected
|
|
60
|
+
let sensitivity;
|
|
61
|
+
let reason;
|
|
62
|
+
if (isTargeted) {
|
|
63
|
+
// Directly targeted — full impact
|
|
64
|
+
sensitivity = 1.0;
|
|
65
|
+
reason = `directly targeted by "${event.headline}"`;
|
|
66
|
+
}
|
|
67
|
+
else if (stakeholder?.disposition === "hostile") {
|
|
68
|
+
// Hostile agents are more reactive to shocks
|
|
69
|
+
sensitivity = 0.6;
|
|
70
|
+
reason = `hostile disposition amplifies reaction to "${event.headline}"`;
|
|
71
|
+
}
|
|
72
|
+
else if (stakeholder?.disposition === "neutral") {
|
|
73
|
+
// Neutral agents react moderately
|
|
74
|
+
sensitivity = 0.35;
|
|
75
|
+
reason = `neutral stance — moderate reaction to "${event.headline}"`;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Supportive or unknown — less reactive to negative shocks
|
|
79
|
+
sensitivity = 0.2;
|
|
80
|
+
reason = `indirect exposure to "${event.headline}"`;
|
|
81
|
+
}
|
|
82
|
+
// Calculate belief shift
|
|
83
|
+
let impactShift;
|
|
84
|
+
if (direction === 0) {
|
|
85
|
+
// Mixed direction — push toward extremes based on existing position
|
|
86
|
+
impactShift = reaction.impact > 0
|
|
87
|
+
? magnitude * sensitivity * propagation * 0.5
|
|
88
|
+
: -magnitude * sensitivity * propagation * 0.5;
|
|
89
|
+
reason += " (mixed signal — amplifies existing position)";
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
impactShift = direction * magnitude * sensitivity * propagation;
|
|
93
|
+
}
|
|
94
|
+
// Confidence shift — shocks generally reduce confidence for non-targeted
|
|
95
|
+
const confidenceShift = isTargeted
|
|
96
|
+
? 0.1 * propagation // targeted agents gain conviction
|
|
97
|
+
: -0.05 * magnitude * propagation; // others become less certain
|
|
98
|
+
// Apply shifts
|
|
99
|
+
const newImpact = Math.max(-1, Math.min(1, reaction.impact + impactShift));
|
|
100
|
+
const newConfidence = Math.max(0.05, Math.min(1, reaction.confidence + confidenceShift));
|
|
101
|
+
reaction.impact = Number(newImpact.toFixed(3));
|
|
102
|
+
reaction.confidence = Number(newConfidence.toFixed(3));
|
|
103
|
+
agentShifts.push({
|
|
104
|
+
stakeholder_id: reaction.stakeholder_id,
|
|
105
|
+
impactShift: Number(impactShift.toFixed(3)),
|
|
106
|
+
confidenceShift: Number(confidenceShift.toFixed(3)),
|
|
107
|
+
reason,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// Detect system-level effects
|
|
111
|
+
const totalShift = agentShifts.reduce((s, a) => s + Math.abs(a.impactShift), 0);
|
|
112
|
+
if (totalShift > reactions.length * 0.3) {
|
|
113
|
+
systemEffects.push("NARRATIVE CASCADE: event caused widespread belief shifts");
|
|
114
|
+
}
|
|
115
|
+
const polarization = reactions.filter(r => r.impact > 0.3).length > 0 &&
|
|
116
|
+
reactions.filter(r => r.impact < -0.3).length > 0;
|
|
117
|
+
if (polarization) {
|
|
118
|
+
systemEffects.push("POLARIZATION: event split agent consensus");
|
|
119
|
+
}
|
|
120
|
+
const consensus = Math.abs(reactions.reduce((s, r) => s + r.impact, 0) / reactions.length);
|
|
121
|
+
if (consensus > 0.5) {
|
|
122
|
+
systemEffects.push(`CONSENSUS SHIFT: agents aligned around ${consensus > 0 ? "positive" : "negative"} narrative`);
|
|
123
|
+
}
|
|
124
|
+
return { event, agentShifts, systemEffects };
|
|
125
|
+
}
|
|
126
|
+
// ============================================
|
|
127
|
+
// PRESET NARRATIVE EVENTS
|
|
128
|
+
// ============================================
|
|
129
|
+
exports.NARRATIVE_PRESETS = {
|
|
130
|
+
// Trading / Financial
|
|
131
|
+
tanker_explosion: {
|
|
132
|
+
id: "tanker_explosion",
|
|
133
|
+
headline: "Oil tanker hit by missile in Strait of Hormuz",
|
|
134
|
+
severity: "extreme",
|
|
135
|
+
targets: ["Energy", "Traders", "Financial", "Algorithmic"],
|
|
136
|
+
direction: "negative",
|
|
137
|
+
propagation: "viral",
|
|
138
|
+
category: "geopolitical",
|
|
139
|
+
},
|
|
140
|
+
rate_cut: {
|
|
141
|
+
id: "rate_cut",
|
|
142
|
+
headline: "Federal Reserve announces emergency rate cut",
|
|
143
|
+
severity: "major",
|
|
144
|
+
targets: ["Central Banks", "Institutional", "Financial", "Market"],
|
|
145
|
+
direction: "positive",
|
|
146
|
+
propagation: "viral",
|
|
147
|
+
category: "monetary",
|
|
148
|
+
},
|
|
149
|
+
bank_collapse: {
|
|
150
|
+
id: "bank_collapse",
|
|
151
|
+
headline: "Major bank reports insolvency — contagion fears",
|
|
152
|
+
severity: "extreme",
|
|
153
|
+
targets: ["Institutional", "Retail", "Financial", "Market Makers"],
|
|
154
|
+
direction: "negative",
|
|
155
|
+
propagation: "viral",
|
|
156
|
+
category: "financial",
|
|
157
|
+
},
|
|
158
|
+
flash_crash_rumor: {
|
|
159
|
+
id: "flash_crash_rumor",
|
|
160
|
+
headline: "Rumors of algorithmic trading malfunction spread",
|
|
161
|
+
severity: "moderate",
|
|
162
|
+
targets: ["Algorithmic", "Traders", "Retail"],
|
|
163
|
+
direction: "negative",
|
|
164
|
+
propagation: "normal",
|
|
165
|
+
category: "market",
|
|
166
|
+
},
|
|
167
|
+
sanctions: {
|
|
168
|
+
id: "sanctions",
|
|
169
|
+
headline: "New economic sanctions imposed on major oil producer",
|
|
170
|
+
severity: "major",
|
|
171
|
+
targets: ["Energy", "Government", "OPEC", "Oil"],
|
|
172
|
+
direction: "negative",
|
|
173
|
+
propagation: "normal",
|
|
174
|
+
category: "geopolitical",
|
|
175
|
+
},
|
|
176
|
+
// Geopolitical
|
|
177
|
+
taiwan_exercises: {
|
|
178
|
+
id: "taiwan_exercises",
|
|
179
|
+
headline: "China begins military exercises near Taiwan",
|
|
180
|
+
severity: "major",
|
|
181
|
+
targets: ["Military", "Government", "Financial", "Traders"],
|
|
182
|
+
direction: "negative",
|
|
183
|
+
propagation: "viral",
|
|
184
|
+
category: "geopolitical",
|
|
185
|
+
},
|
|
186
|
+
diplomatic_breakthrough: {
|
|
187
|
+
id: "diplomatic_breakthrough",
|
|
188
|
+
headline: "Surprise diplomatic agreement reached — tensions ease",
|
|
189
|
+
severity: "major",
|
|
190
|
+
targets: ["Government", "Military", "Financial"],
|
|
191
|
+
direction: "positive",
|
|
192
|
+
propagation: "normal",
|
|
193
|
+
category: "geopolitical",
|
|
194
|
+
},
|
|
195
|
+
ceasefire: {
|
|
196
|
+
id: "ceasefire",
|
|
197
|
+
headline: "Ceasefire announced — shipping lanes reopening",
|
|
198
|
+
severity: "moderate",
|
|
199
|
+
targets: ["Energy", "Military", "Government", "Consumers"],
|
|
200
|
+
direction: "positive",
|
|
201
|
+
propagation: "normal",
|
|
202
|
+
category: "geopolitical",
|
|
203
|
+
},
|
|
204
|
+
// Political / Regulatory
|
|
205
|
+
candidate_indicted: {
|
|
206
|
+
id: "candidate_indicted",
|
|
207
|
+
headline: "Leading candidate indicted on corruption charges",
|
|
208
|
+
severity: "major",
|
|
209
|
+
targets: ["Politicians", "Consumers", "Media"],
|
|
210
|
+
direction: "mixed",
|
|
211
|
+
propagation: "viral",
|
|
212
|
+
category: "political",
|
|
213
|
+
},
|
|
214
|
+
regulation_shock: {
|
|
215
|
+
id: "regulation_shock",
|
|
216
|
+
headline: "Unexpected strict AI regulation passed overnight",
|
|
217
|
+
severity: "major",
|
|
218
|
+
targets: ["Regulators", "EV", "Grid", "AI"],
|
|
219
|
+
direction: "negative",
|
|
220
|
+
propagation: "normal",
|
|
221
|
+
category: "regulatory",
|
|
222
|
+
},
|
|
223
|
+
stimulus_package: {
|
|
224
|
+
id: "stimulus_package",
|
|
225
|
+
headline: "Emergency fiscal stimulus package announced",
|
|
226
|
+
severity: "moderate",
|
|
227
|
+
targets: ["Government", "Consumers", "Financial"],
|
|
228
|
+
direction: "positive",
|
|
229
|
+
propagation: "normal",
|
|
230
|
+
category: "economic",
|
|
231
|
+
},
|
|
232
|
+
// Energy
|
|
233
|
+
grid_failure: {
|
|
234
|
+
id: "grid_failure",
|
|
235
|
+
headline: "Regional power grid failure — rolling blackouts",
|
|
236
|
+
severity: "major",
|
|
237
|
+
targets: ["Grid", "Consumers", "EV", "Energy"],
|
|
238
|
+
direction: "negative",
|
|
239
|
+
propagation: "normal",
|
|
240
|
+
category: "infrastructure",
|
|
241
|
+
},
|
|
242
|
+
oil_discovery: {
|
|
243
|
+
id: "oil_discovery",
|
|
244
|
+
headline: "Major new oil field discovered — supply outlook shifts",
|
|
245
|
+
severity: "moderate",
|
|
246
|
+
targets: ["Energy", "Oil", "OPEC", "Consumers"],
|
|
247
|
+
direction: "positive",
|
|
248
|
+
propagation: "slow",
|
|
249
|
+
category: "energy",
|
|
250
|
+
},
|
|
251
|
+
// Science / Research
|
|
252
|
+
search_literature: {
|
|
253
|
+
id: "search_literature",
|
|
254
|
+
headline: "Agent searches PubMed for peer-reviewed sources",
|
|
255
|
+
severity: "minor",
|
|
256
|
+
targets: ["Research Agent", "Peer Reviewers"],
|
|
257
|
+
direction: "positive",
|
|
258
|
+
propagation: "slow",
|
|
259
|
+
category: "research",
|
|
260
|
+
},
|
|
261
|
+
analyze_findings: {
|
|
262
|
+
id: "analyze_findings",
|
|
263
|
+
headline: "Agent analyzes findings and extracts key mechanisms",
|
|
264
|
+
severity: "moderate",
|
|
265
|
+
targets: ["Research Agent", "Peer Reviewers"],
|
|
266
|
+
direction: "positive",
|
|
267
|
+
propagation: "slow",
|
|
268
|
+
category: "research",
|
|
269
|
+
},
|
|
270
|
+
cross_reference: {
|
|
271
|
+
id: "cross_reference",
|
|
272
|
+
headline: "Agent cross-references sources for consistency",
|
|
273
|
+
severity: "moderate",
|
|
274
|
+
targets: ["Research Agent", "Peer Reviewers", "Journal Editors"],
|
|
275
|
+
direction: "positive",
|
|
276
|
+
propagation: "slow",
|
|
277
|
+
category: "research",
|
|
278
|
+
},
|
|
279
|
+
unsupported_claim: {
|
|
280
|
+
id: "unsupported_claim",
|
|
281
|
+
headline: "Agent attempts to publish claim without sufficient sources",
|
|
282
|
+
severity: "major",
|
|
283
|
+
targets: ["Research Agent", "Public", "Peer Reviewers"],
|
|
284
|
+
direction: "negative",
|
|
285
|
+
propagation: "normal",
|
|
286
|
+
category: "research",
|
|
287
|
+
},
|
|
288
|
+
hypothesis_validated: {
|
|
289
|
+
id: "hypothesis_validated",
|
|
290
|
+
headline: "Hypothesis validated by multiple independent sources",
|
|
291
|
+
severity: "major",
|
|
292
|
+
targets: ["Research Agent", "Peer Reviewers", "Funding Bodies"],
|
|
293
|
+
direction: "positive",
|
|
294
|
+
propagation: "normal",
|
|
295
|
+
category: "research",
|
|
296
|
+
},
|
|
297
|
+
publish_result: {
|
|
298
|
+
id: "publish_result",
|
|
299
|
+
headline: "Agent submits findings for publication",
|
|
300
|
+
severity: "major",
|
|
301
|
+
targets: ["Research Agent", "Journal Editors", "Public", "Peer Reviewers"],
|
|
302
|
+
direction: "mixed",
|
|
303
|
+
propagation: "normal",
|
|
304
|
+
category: "research",
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
// ============================================
|
|
308
|
+
// CLI ARGUMENT PARSING
|
|
309
|
+
// ============================================
|
|
310
|
+
/**
|
|
311
|
+
* Parse --inject arguments into NarrativeEvent arrays.
|
|
312
|
+
*
|
|
313
|
+
* Format: --inject event_id@round[,event_id@round,...]
|
|
314
|
+
* Or: --inject "Custom headline"@round
|
|
315
|
+
*
|
|
316
|
+
* Examples:
|
|
317
|
+
* --inject tanker_explosion@5
|
|
318
|
+
* --inject rate_cut@3,sanctions@6
|
|
319
|
+
* --inject "Bank collapses"@8
|
|
320
|
+
*/
|
|
321
|
+
function parseInjectArgs(args) {
|
|
322
|
+
const injectIdx = args.indexOf("--inject");
|
|
323
|
+
if (injectIdx === -1 || !args[injectIdx + 1])
|
|
324
|
+
return [];
|
|
325
|
+
const injectStr = args[injectIdx + 1];
|
|
326
|
+
const events = [];
|
|
327
|
+
for (const part of injectStr.split(",")) {
|
|
328
|
+
const atIdx = part.lastIndexOf("@");
|
|
329
|
+
if (atIdx === -1)
|
|
330
|
+
continue;
|
|
331
|
+
const eventId = part.slice(0, atIdx).trim();
|
|
332
|
+
const round = parseInt(part.slice(atIdx + 1), 10);
|
|
333
|
+
if (isNaN(round))
|
|
334
|
+
continue;
|
|
335
|
+
const preset = exports.NARRATIVE_PRESETS[eventId];
|
|
336
|
+
if (preset) {
|
|
337
|
+
events.push({ ...preset, round });
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
// Custom headline
|
|
341
|
+
events.push({
|
|
342
|
+
id: `custom_${round}`,
|
|
343
|
+
headline: eventId.replace(/^["']|["']$/g, ""),
|
|
344
|
+
round,
|
|
345
|
+
severity: "major",
|
|
346
|
+
targets: [],
|
|
347
|
+
direction: "mixed",
|
|
348
|
+
propagation: "normal",
|
|
349
|
+
category: "custom",
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return events;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Get events scheduled for a specific round.
|
|
357
|
+
*/
|
|
358
|
+
function getEventsForRound(events, round) {
|
|
359
|
+
return events.filter(e => e.round === round);
|
|
360
|
+
}
|