@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.
Files changed (53) hide show
  1. package/README.md +562 -68
  2. package/dist/adapters/mirofish.js +461 -0
  3. package/dist/adapters/scienceclaw.js +750 -0
  4. package/dist/assets/index-B64NuIXu.css +1 -0
  5. package/dist/assets/index-DbzSnYxr.js +532 -0
  6. package/dist/assets/mirotir-logo-DUexumBH.svg +185 -0
  7. package/dist/assets/reportEngine-DKWTrP6-.js +1 -0
  8. package/dist/components/ConstraintsPanel.js +11 -0
  9. package/dist/components/StakeholderBuilder.js +32 -0
  10. package/dist/components/ui/badge.js +24 -0
  11. package/dist/components/ui/button.js +70 -0
  12. package/dist/components/ui/card.js +57 -0
  13. package/dist/components/ui/input.js +44 -0
  14. package/dist/components/ui/label.js +45 -0
  15. package/dist/components/ui/select.js +70 -0
  16. package/dist/engine/aiProvider.js +681 -0
  17. package/dist/engine/auditTrace.js +352 -0
  18. package/dist/engine/behavioralAnalysis.js +605 -0
  19. package/dist/engine/cli.js +1408 -299
  20. package/dist/engine/dynamicsGovernance.js +588 -0
  21. package/dist/engine/fullGovernedLoop.js +367 -0
  22. package/dist/engine/governance.js +8 -3
  23. package/dist/engine/governedSimulation.js +114 -17
  24. package/dist/engine/index.js +56 -1
  25. package/dist/engine/liveAdapter.js +342 -0
  26. package/dist/engine/liveVisualizer.js +4284 -0
  27. package/dist/engine/metrics/science.metrics.js +335 -0
  28. package/dist/engine/narrativeInjection.js +360 -0
  29. package/dist/engine/policyEnforcement.js +1611 -0
  30. package/dist/engine/policyEngine.js +799 -0
  31. package/dist/engine/primeRadiant.js +540 -0
  32. package/dist/engine/reasoningEngine.js +57 -3
  33. package/dist/engine/reportEngine.js +97 -0
  34. package/dist/engine/scenarioCapsule.js +56 -0
  35. package/dist/engine/scenarioComparison.js +463 -0
  36. package/dist/engine/scenarioLibrary.js +248 -0
  37. package/dist/engine/swarmSimulation.js +54 -1
  38. package/dist/engine/worldComparison.js +358 -0
  39. package/dist/engine/worldStorage.js +232 -0
  40. package/dist/favicon.ico +0 -0
  41. package/dist/index.html +23 -0
  42. package/dist/lib/reasoningEngine.js +290 -0
  43. package/dist/lib/simulationAdapter.js +686 -0
  44. package/dist/lib/swarmParser.js +291 -0
  45. package/dist/lib/types.js +2 -0
  46. package/dist/lib/utils.js +8 -0
  47. package/dist/placeholder.svg +1 -0
  48. package/dist/robots.txt +14 -0
  49. package/dist/runtime/govern.js +473 -0
  50. package/dist/runtime/index.js +75 -0
  51. package/dist/runtime/types.js +11 -0
  52. package/package.json +17 -12
  53. 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
+ }