@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,295 @@
1
+ "use strict";
2
+ /**
3
+ * MiroFish Integration Adapter
4
+ *
5
+ * Connects Mirotir to MiroFish swarm simulations as an EXTERNAL SERVICE.
6
+ *
7
+ * POSITIONING:
8
+ * MiroFish shows what might happen → the spectacle
9
+ * Mirotir helps you decide what to do → the brain
10
+ *
11
+ * "MiroFish simulates the crowd. Mirotir reasons about the decision."
12
+ *
13
+ * LICENSE COMPLIANCE:
14
+ * MiroFish is called as an external service via its REST API.
15
+ * We do NOT embed, copy, or redistribute MiroFish code.
16
+ * We do NOT use MiroFish internals, algorithms, or data structures.
17
+ * The integration is purely through the public API contract.
18
+ * This is the same relationship as using Stripe's API — we call it, we don't own it.
19
+ *
20
+ * ARCHITECTURE:
21
+ * Scenario → Echelon reasoning → strategies generated
22
+ * ↓
23
+ * MiroFish simulation runs
24
+ * ↓
25
+ * Echelon evaluates outcomes
26
+ *
27
+ * MiroFish = world engine (emergent behavior, swarms, crowds)
28
+ * Echelon = decision engine (structured reasoning, recommendations)
29
+ *
30
+ * FALLBACK:
31
+ * When MiroFish is not available, we use Echelon's native
32
+ * lightweight reaction model (swarmSimulation.ts).
33
+ * The API contract is the same either way.
34
+ */
35
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
36
+ if (k2 === undefined) k2 = k;
37
+ var desc = Object.getOwnPropertyDescriptor(m, k);
38
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
39
+ desc = { enumerable: true, get: function() { return m[k]; } };
40
+ }
41
+ Object.defineProperty(o, k2, desc);
42
+ }) : (function(o, m, k, k2) {
43
+ if (k2 === undefined) k2 = k;
44
+ o[k2] = m[k];
45
+ }));
46
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
47
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
48
+ }) : function(o, v) {
49
+ o["default"] = v;
50
+ });
51
+ var __importStar = (this && this.__importStar) || (function () {
52
+ var ownKeys = function(o) {
53
+ ownKeys = Object.getOwnPropertyNames || function (o) {
54
+ var ar = [];
55
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
56
+ return ar;
57
+ };
58
+ return ownKeys(o);
59
+ };
60
+ return function (mod) {
61
+ if (mod && mod.__esModule) return mod;
62
+ var result = {};
63
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
64
+ __setModuleDefault(result, mod);
65
+ return result;
66
+ };
67
+ })();
68
+ Object.defineProperty(exports, "__esModule", { value: true });
69
+ exports.MiroFishError = exports.MiroFishClient = exports.DEFAULT_MIROFISH_CONFIG = void 0;
70
+ exports.stakeholdersToMiroFishAgents = stakeholdersToMiroFishAgents;
71
+ exports.miroFishResultToSwarmResult = miroFishResultToSwarmResult;
72
+ exports.buildMiroFishRequest = buildMiroFishRequest;
73
+ exports.configureMiroFish = configureMiroFish;
74
+ exports.getMiroFishClient = getMiroFishClient;
75
+ exports.runUnifiedSimulation = runUnifiedSimulation;
76
+ /**
77
+ * Default MiroFish configuration.
78
+ * Assumes local Docker container on default port.
79
+ */
80
+ exports.DEFAULT_MIROFISH_CONFIG = {
81
+ endpoint: "http://localhost:8080",
82
+ timeout: 30000,
83
+ fallbackToNative: true,
84
+ };
85
+ // ============================================
86
+ // MIROFISH CLIENT
87
+ // ============================================
88
+ /**
89
+ * MiroFish API client.
90
+ * Calls MiroFish as an external service via REST API.
91
+ */
92
+ class MiroFishClient {
93
+ config;
94
+ constructor(config) {
95
+ this.config = { ...exports.DEFAULT_MIROFISH_CONFIG, ...config };
96
+ }
97
+ /**
98
+ * Check if the MiroFish service is available.
99
+ */
100
+ async isAvailable() {
101
+ try {
102
+ const response = await fetch(`${this.config.endpoint}/health`, {
103
+ method: "GET",
104
+ signal: AbortSignal.timeout(5000),
105
+ });
106
+ return response.ok;
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
112
+ /**
113
+ * Run a simulation against the MiroFish service.
114
+ */
115
+ async simulate(request) {
116
+ const headers = {
117
+ "Content-Type": "application/json",
118
+ };
119
+ if (this.config.apiKey) {
120
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
121
+ }
122
+ const response = await fetch(`${this.config.endpoint}/simulate`, {
123
+ method: "POST",
124
+ headers,
125
+ body: JSON.stringify(request),
126
+ signal: AbortSignal.timeout(this.config.timeout ?? 30000),
127
+ });
128
+ if (!response.ok) {
129
+ throw new MiroFishError(`MiroFish simulation failed: ${response.status} ${response.statusText}`, response.status);
130
+ }
131
+ return response.json();
132
+ }
133
+ /**
134
+ * Get the MiroFish service configuration.
135
+ */
136
+ getConfig() {
137
+ return this.config;
138
+ }
139
+ }
140
+ exports.MiroFishClient = MiroFishClient;
141
+ /**
142
+ * MiroFish-specific error.
143
+ */
144
+ class MiroFishError extends Error {
145
+ statusCode;
146
+ constructor(message, statusCode) {
147
+ super(message);
148
+ this.name = "MiroFishError";
149
+ this.statusCode = statusCode;
150
+ }
151
+ }
152
+ exports.MiroFishError = MiroFishError;
153
+ // ============================================
154
+ // TRANSLATION LAYER
155
+ // ============================================
156
+ /**
157
+ * Convert Mirotir stakeholders to MiroFish agents.
158
+ */
159
+ function stakeholdersToMiroFishAgents(stakeholders) {
160
+ return stakeholders.map((s) => ({
161
+ id: s.id,
162
+ role: s.description ?? s.id,
163
+ disposition: s.disposition,
164
+ priorities: s.priorities,
165
+ }));
166
+ }
167
+ /**
168
+ * Convert MiroFish simulation results to Mirotir's SwarmSimulationResult format.
169
+ *
170
+ * This is the key translation — MiroFish provides the raw emergent behavior,
171
+ * and we normalize it into the format that Echelon's reasoning engine consumes.
172
+ *
173
+ * MiroFish shows WHAT happens.
174
+ * Echelon reasons about WHAT IT MEANS.
175
+ */
176
+ function miroFishResultToSwarmResult(mfResult) {
177
+ // Convert MiroFish steps to SwarmRounds
178
+ const rounds = mfResult.steps.map((step) => {
179
+ const reactions = step.agent_actions.map((action) => ({
180
+ stakeholder_id: action.agent_id,
181
+ reaction: action.action,
182
+ confidence: action.confidence,
183
+ impact: action.sentiment,
184
+ trigger: action.reasoning ?? "MiroFish emergent behavior",
185
+ }));
186
+ return {
187
+ round: step.step,
188
+ reactions,
189
+ emergent_dynamics: step.system_events,
190
+ };
191
+ });
192
+ // Determine trajectory from MiroFish final state
193
+ let trajectory;
194
+ if (mfResult.final_state.convergence && mfResult.final_state.stability > 0.7) {
195
+ trajectory = "converging";
196
+ }
197
+ else if (mfResult.final_state.convergence) {
198
+ trajectory = "stabilizing";
199
+ }
200
+ else if (mfResult.final_state.stability < 0.3) {
201
+ trajectory = "escalating";
202
+ }
203
+ else {
204
+ trajectory = "diverging";
205
+ }
206
+ // Extract inflection points from emergent patterns
207
+ const inflection_points = mfResult.emergent_patterns ?? [
208
+ "MiroFish simulation completed — see rounds for detailed agent behaviors",
209
+ ];
210
+ return {
211
+ rounds,
212
+ trajectory,
213
+ inflection_points,
214
+ };
215
+ }
216
+ /**
217
+ * Build a MiroFish simulation request from Mirotir inputs.
218
+ */
219
+ function buildMiroFishRequest(scenario, stakeholders, config) {
220
+ return {
221
+ scenario,
222
+ agents: stakeholdersToMiroFishAgents(config.simulate_stakeholders
223
+ ? stakeholders.filter((s) => config.simulate_stakeholders.includes(s.id))
224
+ : stakeholders),
225
+ steps: config.rounds ?? 3,
226
+ parameters: {
227
+ model: config.reaction_model ?? "mixed",
228
+ pressure: 0.5,
229
+ allow_coalitions: true,
230
+ },
231
+ };
232
+ }
233
+ /**
234
+ * Singleton MiroFish client instance.
235
+ * Configured via setMiroFishConfig().
236
+ */
237
+ let miroFishClient = null;
238
+ /**
239
+ * Configure the MiroFish connection.
240
+ * Call this at startup if MiroFish is available.
241
+ */
242
+ function configureMiroFish(config) {
243
+ miroFishClient = new MiroFishClient(config);
244
+ }
245
+ /**
246
+ * Get the current MiroFish client (if configured).
247
+ */
248
+ function getMiroFishClient() {
249
+ return miroFishClient;
250
+ }
251
+ /**
252
+ * Run simulation through unified interface.
253
+ *
254
+ * Strategy:
255
+ * 1. If MiroFish is configured and available → use it
256
+ * 2. If MiroFish fails and fallback is enabled → use Echelon-native
257
+ * 3. If MiroFish is not configured → use Echelon-native
258
+ */
259
+ async function runUnifiedSimulation(scenario, stakeholders, paths, swarmConfig, miroFishOverride) {
260
+ // Determine which client to use
261
+ const client = miroFishOverride
262
+ ? new MiroFishClient(miroFishOverride)
263
+ : miroFishClient;
264
+ // Try MiroFish first if configured
265
+ if (client) {
266
+ try {
267
+ const available = await client.isAvailable();
268
+ if (available) {
269
+ const request = buildMiroFishRequest(scenario, stakeholders, swarmConfig);
270
+ const mfResult = await client.simulate(request);
271
+ const result = miroFishResultToSwarmResult(mfResult);
272
+ return {
273
+ result,
274
+ source: "mirofish",
275
+ mirofish_simulation_id: mfResult.simulation_id,
276
+ };
277
+ }
278
+ }
279
+ catch (error) {
280
+ const fallback = miroFishOverride?.fallbackToNative ?? client.getConfig().fallbackToNative ?? true;
281
+ if (!fallback) {
282
+ throw error;
283
+ }
284
+ // Log and fall through to native simulation
285
+ console.warn("[MiroFish] Simulation failed, falling back to Echelon-native:", error instanceof Error ? error.message : "Unknown error");
286
+ }
287
+ }
288
+ // Fall back to Echelon-native simulation
289
+ const { runSwarmSimulation } = await Promise.resolve().then(() => __importStar(require("./swarmSimulation")));
290
+ const result = await runSwarmSimulation(scenario, stakeholders, paths, swarmConfig);
291
+ return {
292
+ result,
293
+ source: "echelon-native",
294
+ };
295
+ }