@neuroverseos/nv-sim 0.1.7 → 0.1.10

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 (42) hide show
  1. package/README.md +375 -197
  2. package/connectors/nv_mirofish_wrapper.py +841 -0
  3. package/connectors/nv_scienceclaw_wrapper.py +453 -0
  4. package/dist/adapters/scienceclaw.js +52 -2
  5. package/dist/assets/index-B43_0HyO.css +1 -0
  6. package/dist/assets/index-CdghpsS8.js +595 -0
  7. package/dist/assets/{reportEngine-BVdQ2_nW.js → reportEngine-CYSZfooa.js} +1 -1
  8. package/dist/connectors/nv-scienceclaw-post.js +376 -0
  9. package/dist/engine/aiProvider.js +82 -3
  10. package/dist/engine/analyzer.js +12 -24
  11. package/dist/engine/chaosEngine.js +3 -9
  12. package/dist/engine/cli.js +123 -218
  13. package/dist/engine/dynamicsGovernance.js +4 -0
  14. package/dist/engine/fullGovernedLoop.js +16 -1
  15. package/dist/engine/goalEngine.js +3 -4
  16. package/dist/engine/governance.js +18 -0
  17. package/dist/engine/index.js +19 -29
  18. package/dist/engine/intentTranslator.js +281 -0
  19. package/dist/engine/liveAdapter.js +100 -18
  20. package/dist/engine/liveVisualizer.js +2656 -866
  21. package/dist/engine/narrativeInjection.js +78 -89
  22. package/dist/engine/policyEngine.js +171 -58
  23. package/dist/engine/primeRadiant.js +2 -8
  24. package/dist/engine/reasoningEngine.js +2 -7
  25. package/dist/engine/scenarioCapsule.js +77 -133
  26. package/dist/engine/scenarioLibrary.js +52 -131
  27. package/dist/engine/swarmSimulation.js +1 -9
  28. package/dist/engine/worldBridge.js +22 -8
  29. package/dist/engine/worldComparison.js +12 -25
  30. package/dist/index.html +2 -2
  31. package/dist/lib/reasoningEngine.js +17 -1
  32. package/dist/lib/simulationAdapter.js +11 -11
  33. package/dist/lib/swarmParser.js +1 -1
  34. package/dist/runtime/govern.js +160 -7
  35. package/dist/runtime/index.js +1 -4
  36. package/dist/runtime/types.js +91 -0
  37. package/package.json +23 -6
  38. package/dist/adapters/mirofish.js +0 -461
  39. package/dist/assets/index-CHmUN8s0.js +0 -532
  40. package/dist/assets/index-DWgMnB7I.css +0 -1
  41. package/dist/assets/mirotir-logo-DUexumBH.svg +0 -185
  42. package/dist/engine/mirofish.js +0 -295
@@ -26,23 +26,18 @@ function resolveWorld(presetId) {
26
26
  const saved = resolveSavedWorld(presetId);
27
27
  if (saved)
28
28
  return saved;
29
- if (presetId === "trading" || presetId === "flash_crash") {
30
- return {
31
- id: "trading-flash-crash",
32
- title: "Flash Crash — Algorithmic Cascade",
33
- scenario: governedSimulation_1.TRADING_DEMO.scenario.scenario,
34
- stakeholders: governedSimulation_1.TRADING_DEMO.scenario.stakeholders,
35
- assumptions: governedSimulation_1.TRADING_DEMO.scenario.assumptions,
36
- constraints: governedSimulation_1.TRADING_DEMO.scenario.constraints,
37
- swarm: governedSimulation_1.TRADING_DEMO.scenario.swarm,
38
- depth: governedSimulation_1.TRADING_DEMO.scenario.depth,
39
- world: governedSimulation_1.TRADING_DEMO.world,
40
- paths: governedSimulation_1.TRADING_DEMO.paths,
41
- };
42
- }
43
- const template = scenarioCapsule_1.SCENARIO_TEMPLATES[presetId];
29
+ // Legacy aliases map old world IDs to the 2 active presets
30
+ const legacyMap = {
31
+ trading: "social_simulation",
32
+ flash_crash: "social_simulation",
33
+ strait_of_hormuz: "social_simulation",
34
+ gas_price_spike: "social_simulation",
35
+ ai_regulation_crisis: "social_simulation",
36
+ };
37
+ const resolvedId = legacyMap[presetId] ?? presetId;
38
+ const template = scenarioCapsule_1.SCENARIO_TEMPLATES[resolvedId];
44
39
  if (!template) {
45
- throw new Error(`Unknown preset: ${presetId}. Available: trading, ${Object.keys(scenarioCapsule_1.SCENARIO_TEMPLATES).join(", ")}`);
40
+ throw new Error(`Unknown preset: ${presetId}. Available: ${Object.keys(scenarioCapsule_1.SCENARIO_TEMPLATES).join(", ")}`);
46
41
  }
47
42
  if (!template.world?.inline_definition) {
48
43
  throw new Error(`Preset "${presetId}" has no world definition.`);
@@ -261,15 +256,7 @@ function buildWorldNarrative(worldA, worldB, metricsA, metricsB, moreStable) {
261
256
  * Get all available world presets for the comparison picker.
262
257
  */
263
258
  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
- ];
259
+ const worlds = [];
273
260
  for (const [key, template] of Object.entries(scenarioCapsule_1.SCENARIO_TEMPLATES)) {
274
261
  if (template.world?.inline_definition) {
275
262
  worlds.push({
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-CHmUN8s0.js"></script>
18
- <link rel="stylesheet" crossorigin href="/assets/index-DWgMnB7I.css">
17
+ <script type="module" crossorigin src="/assets/index-CdghpsS8.js"></script>
18
+ <link rel="stylesheet" crossorigin href="/assets/index-B43_0HyO.css">
19
19
  </head>
20
20
  <body>
21
21
  <div id="root"></div>
@@ -276,7 +276,23 @@ async function generateReasoning(scenario, sliders, options) {
276
276
  mode: options?.mode,
277
277
  goal: options?.goal,
278
278
  };
279
- const response = await (0, index_1.processReasonRequest)(request);
279
+ // All governance evaluation goes through the server.
280
+ // The browser NEVER evaluates governance locally — the server runs
281
+ // @neuroverseos/governance with full Node.js access and real enforcement.
282
+ const apiUrl = `${window.location.protocol}//${window.location.hostname}:3456/api/v1/reason`;
283
+ let response;
284
+ try {
285
+ const res = await fetch(apiUrl, {
286
+ method: 'POST',
287
+ headers: { 'Content-Type': 'application/json' },
288
+ body: JSON.stringify(request),
289
+ });
290
+ response = await res.json();
291
+ }
292
+ catch (err) {
293
+ console.error('Failed to reach governance server:', err);
294
+ response = { status: 'error' };
295
+ }
280
296
  if (response.status === 'error') {
281
297
  return {
282
298
  paths: [],
@@ -5,7 +5,7 @@
5
5
  * Converts output from ANY simulation engine into a normalized schema
6
6
  * that Echelon can reason about. Supported formats:
7
7
  *
8
- * - MiroFish (JSON with agents + emergent_behaviors)
8
+ * - Agent Swarm (JSON with agents + emergent_behaviors)
9
9
  * - NetLogo (tick-based key:value output)
10
10
  * - Mesa (Python agent action logs)
11
11
  * - AnyLogic / CSV (time-series tabular data)
@@ -37,8 +37,8 @@ function detectFormat(input) {
37
37
  if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
38
38
  try {
39
39
  const data = JSON.parse(trimmed);
40
- if (looksLikeMiroFish(data))
41
- return "mirofish";
40
+ if (looksLikeAgentSwarm(data))
41
+ return "agent_swarm";
42
42
  return "generic_json";
43
43
  }
44
44
  catch {
@@ -56,7 +56,7 @@ function detectFormat(input) {
56
56
  return "mesa";
57
57
  return "freeform_text";
58
58
  }
59
- function looksLikeMiroFish(data) {
59
+ function looksLikeAgentSwarm(data) {
60
60
  if (typeof data !== "object" || data === null)
61
61
  return false;
62
62
  const d = data;
@@ -107,8 +107,8 @@ function looksLikeMesa(text) {
107
107
  function parseSimulation(input) {
108
108
  const format = detectFormat(input);
109
109
  switch (format) {
110
- case "mirofish":
111
- return parseMiroFish(input);
110
+ case "agent_swarm":
111
+ return parseAgentSwarm(input);
112
112
  case "netlogo":
113
113
  return parseNetLogo(input);
114
114
  case "mesa":
@@ -121,8 +121,8 @@ function parseSimulation(input) {
121
121
  return parseFreeformText(input);
122
122
  }
123
123
  }
124
- // ── MiroFish Parser ──────────────────────────────────
125
- function parseMiroFish(input) {
124
+ // ── Agent Swarm Parser ──────────────────────────────────
125
+ function parseAgentSwarm(input) {
126
126
  const data = JSON.parse(input.trim());
127
127
  const agents = [];
128
128
  const events = [];
@@ -172,7 +172,7 @@ function parseMiroFish(input) {
172
172
  }
173
173
  }
174
174
  }
175
- // Parse steps (MiroFish simulation output)
175
+ // Parse steps (simulation output)
176
176
  if (data.steps && Array.isArray(data.steps)) {
177
177
  for (const step of data.steps) {
178
178
  const t = step.step ?? step.round ?? timeline.length;
@@ -196,7 +196,7 @@ function parseMiroFish(input) {
196
196
  }
197
197
  }
198
198
  return {
199
- sourceFormat: "mirofish",
199
+ sourceFormat: "agent_swarm",
200
200
  agents,
201
201
  events,
202
202
  stateChanges,
@@ -677,7 +677,7 @@ function extractAttributes(obj, exclude) {
677
677
  // FORMAT LABELS (for UI)
678
678
  // ============================================
679
679
  exports.FORMAT_LABELS = {
680
- mirofish: "MiroFish",
680
+ agent_swarm: "Agent Swarm",
681
681
  netlogo: "NetLogo",
682
682
  mesa: "Mesa (Python)",
683
683
  anylogic_csv: "AnyLogic / CSV",
@@ -54,7 +54,7 @@ function seededRandom(seed) {
54
54
  * Parse simulation output into a SwarmResult.
55
55
  *
56
56
  * Uses the universal simulation adapter to normalize input from
57
- * MiroFish, NetLogo, Mesa, AnyLogic/CSV, generic JSON, or freeform text,
57
+ * Agent Swarm, NetLogo, Mesa, AnyLogic/CSV, generic JSON, or freeform text,
58
58
  * then generates structured reasoning overlay from the normalized data.
59
59
  *
60
60
  * Returns both the SwarmResult (for UI) and the detected format.
@@ -79,22 +79,78 @@ function createGovernor(config) {
79
79
  blocked: 0,
80
80
  modified: 0,
81
81
  paused: 0,
82
+ rewarded: 0,
83
+ penalized: 0,
82
84
  rulesFired: 0,
83
85
  };
86
+ // Per-agent behavior states for incentive tracking
87
+ const agentStates = new Map();
88
+ function getOrCreateAgentState(agentId) {
89
+ if (!agentStates.has(agentId)) {
90
+ agentStates.set(agentId, {
91
+ agentId,
92
+ cooldownRemaining: 0,
93
+ influence: 1.0,
94
+ rewardMultiplier: 1.0,
95
+ totalPenalties: 0,
96
+ totalRewards: 0,
97
+ });
98
+ }
99
+ return agentStates.get(agentId);
100
+ }
84
101
  function evaluate(action, worldState) {
85
102
  stats.totalEvaluations++;
103
+ const agentState = getOrCreateAgentState(action.agentId);
104
+ // ── Cooldown check: frozen agents are auto-penalized ──
105
+ if (agentState.cooldownRemaining > 0) {
106
+ agentState.cooldownRemaining--;
107
+ stats.penalized++;
108
+ return {
109
+ status: "PENALIZE",
110
+ action: null,
111
+ reason: `Agent frozen: ${agentState.cooldownRemaining + 1} round(s) remaining on cooldown`,
112
+ rulesFired: [],
113
+ confidence: 1,
114
+ timestamp: Date.now(),
115
+ consequence: { type: "cooldown", rounds: agentState.cooldownRemaining + 1, description: "Agent still in cooldown period" },
116
+ };
117
+ }
86
118
  const rulesFired = [];
87
119
  let currentMagnitude = action.magnitude;
88
120
  let wasModified = false;
121
+ let penalizeRule = null;
122
+ let rewardRule = null;
89
123
  // --- Evaluate action against each invariant ---
90
124
  for (const inv of world.invariants) {
91
125
  if (!inv.enforceable) {
92
- // Advisory: log but don't block
126
+ // Check for reward patterns — advisory rules that match positively
127
+ if (matchesRewardPattern(inv.description, action)) {
128
+ rewardRule = { inv, reward: { type: "boost_influence", magnitude: 0.1, description: inv.description } };
129
+ rulesFired.push({
130
+ id: inv.id,
131
+ description: inv.description,
132
+ effect: "rewarded",
133
+ impactReduction: 0,
134
+ });
135
+ }
136
+ else {
137
+ rulesFired.push({
138
+ id: inv.id,
139
+ description: inv.description,
140
+ effect: "monitored",
141
+ impactReduction: 0,
142
+ });
143
+ }
144
+ continue;
145
+ }
146
+ // Check for penalize patterns before standard evaluation
147
+ if (matchesPenalizePattern(inv.description, action)) {
148
+ penalizeRule = { inv, consequence: { type: "freeze", rounds: 1, magnitude: 0.5, description: inv.description } };
93
149
  rulesFired.push({
94
150
  id: inv.id,
95
151
  description: inv.description,
96
- effect: "monitored",
97
- impactReduction: 0,
152
+ effect: "penalized",
153
+ impactReduction: 1,
98
154
  });
99
155
  continue;
100
156
  }
@@ -114,6 +170,58 @@ function createGovernor(config) {
114
170
  wasModified = true;
115
171
  }
116
172
  }
173
+ // ── PENALIZE verdict ──
174
+ if (penalizeRule) {
175
+ stats.penalized++;
176
+ stats.rulesFired++;
177
+ const consequence = penalizeRule.consequence;
178
+ agentState.totalPenalties++;
179
+ agentState.cooldownRemaining = consequence.rounds ?? 1;
180
+ agentState.influence = Math.max(0, agentState.influence - (consequence.magnitude ?? 0.2));
181
+ const verdict = {
182
+ status: "PENALIZE",
183
+ action: null,
184
+ reason: buildReason("penalized", action, rulesFired),
185
+ rulesFired,
186
+ confidence: computeConfidence(rulesFired, 1),
187
+ timestamp: Date.now(),
188
+ consequence,
189
+ };
190
+ if (audit) {
191
+ audit.logVerdict({
192
+ agent: action.agentId, action: action.description, actionType: action.type,
193
+ verdict: "BLOCK", reason: verdict.reason, confidence: verdict.confidence,
194
+ rulesFired: rulesFired.map((r) => ({ id: r.id, description: r.description, effect: r.effect, impactReduction: r.impactReduction })),
195
+ });
196
+ }
197
+ return verdict;
198
+ }
199
+ // ── REWARD verdict (only when no rules blocked/modified the action) ──
200
+ if (rewardRule && !wasModified) {
201
+ stats.rewarded++;
202
+ stats.rulesFired++;
203
+ const reward = rewardRule.reward;
204
+ agentState.totalRewards++;
205
+ agentState.influence = Math.min(2.0, agentState.influence + (reward.magnitude ?? 0.1));
206
+ agentState.rewardMultiplier = Math.min(3.0, agentState.rewardMultiplier + 0.05);
207
+ const verdict = {
208
+ status: "REWARD",
209
+ action: action,
210
+ reason: buildReason("rewarded", action, rulesFired),
211
+ rulesFired,
212
+ confidence: computeConfidence(rulesFired, 0),
213
+ timestamp: Date.now(),
214
+ reward,
215
+ };
216
+ if (audit) {
217
+ audit.logVerdict({
218
+ agent: action.agentId, action: action.description, actionType: action.type,
219
+ verdict: "ALLOW", reason: verdict.reason, confidence: verdict.confidence,
220
+ rulesFired: rulesFired.map((r) => ({ id: r.id, description: r.description, effect: r.effect, impactReduction: r.impactReduction })),
221
+ });
222
+ }
223
+ return verdict;
224
+ }
117
225
  // --- Determine final verdict ---
118
226
  stats.rulesFired += rulesFired.filter((r) => r.effect !== "monitored").length;
119
227
  const totalReduction = 1 - (currentMagnitude / Math.max(action.magnitude, 0.001));
@@ -121,28 +229,24 @@ function createGovernor(config) {
121
229
  let reason;
122
230
  let outputAction;
123
231
  if (totalReduction > 0.85) {
124
- // Action is essentially killed
125
232
  status = "BLOCK";
126
233
  reason = buildReason("blocked", action, rulesFired);
127
234
  outputAction = null;
128
235
  stats.blocked++;
129
236
  }
130
237
  else if (totalReduction > 0.5) {
131
- // Action is significantly reduced — pause for review
132
238
  status = "PAUSE";
133
239
  reason = buildReason("paused", action, rulesFired);
134
240
  outputAction = { ...action, magnitude: Number(currentMagnitude.toFixed(3)) };
135
241
  stats.paused++;
136
242
  }
137
243
  else if (wasModified && totalReduction > 0.05) {
138
- // Action was modified but still largely allowed
139
244
  status = "MODIFY";
140
245
  reason = buildReason("modified", action, rulesFired);
141
246
  outputAction = { ...action, magnitude: Number(currentMagnitude.toFixed(3)) };
142
247
  stats.modified++;
143
248
  }
144
249
  else {
145
- // Clean pass
146
250
  status = "ALLOW";
147
251
  reason = rulesFired.length > 0
148
252
  ? `Allowed — ${rulesFired.filter((r) => r.effect === "monitored").length} advisory rule(s) noted`
@@ -185,10 +289,22 @@ function createGovernor(config) {
185
289
  health = (0, policyEngine_1.validatePolicy)(parsed);
186
290
  world = (0, policyEngine_1.policyToWorld)(parsed);
187
291
  }
292
+ /** Decrement cooldowns for all tracked agents — call once per round */
293
+ function tickCooldowns() {
294
+ for (const [, state] of agentStates) {
295
+ state.cooldownRemaining = Math.max(0, state.cooldownRemaining - 1);
296
+ }
297
+ }
298
+ /** Get all agent behavior states */
299
+ function getAgentStates() {
300
+ return agentStates;
301
+ }
188
302
  return {
189
303
  evaluate,
190
304
  evaluateBatch,
191
305
  updatePolicy,
306
+ tickCooldowns,
307
+ getAgentStates,
192
308
  get policy() {
193
309
  return {
194
310
  ruleCount: parsed.summary.total,
@@ -384,7 +500,44 @@ function buildReason(type, action, rulesFired) {
384
500
  return `Paused by "${primary.description}"${others} — action "${action.description}" requires review`;
385
501
  case "modified":
386
502
  return `Modified by "${primary.description}"${others} — magnitude reduced from ${action.magnitude.toFixed(2)} to stay within policy limits`;
503
+ case "penalized":
504
+ return `Penalized by "${primary.description}"${others} — agent "${action.agentId}" frozen for violating governance`;
505
+ case "rewarded":
506
+ return `Rewarded by "${primary.description}"${others} — agent "${action.agentId}" demonstrated compliant behavior`;
507
+ }
508
+ }
509
+ // ============================================
510
+ // INCENTIVE PATTERN MATCHING
511
+ // ============================================
512
+ /**
513
+ * Detects penalize patterns in rule descriptions.
514
+ * Rules that say "penalize", "freeze", "suspend", "cooldown" for matching actions.
515
+ */
516
+ function matchesPenalizePattern(ruleDesc, action) {
517
+ const desc = ruleDesc.toLowerCase();
518
+ const actionDesc = action.description.toLowerCase();
519
+ const actionType = action.type.toLowerCase();
520
+ // Must have penalize/freeze/suspend keyword
521
+ if (!matchesPattern(desc, ["penalize", "freeze", "suspend", "cooldown", "punish", "sanction"])) {
522
+ return false;
523
+ }
524
+ // Extract what should be penalized
525
+ const actionTerms = extractActionTerms(desc, ["penalize", "freeze", "suspend", "cooldown", "punish", "sanction"]);
526
+ return actionTerms.some((term) => actionDesc.includes(term) || actionType.includes(term));
527
+ }
528
+ /**
529
+ * Detects reward patterns in rule descriptions.
530
+ * Rules that say "reward", "boost", "incentivize", "encourage" for matching actions.
531
+ */
532
+ function matchesRewardPattern(ruleDesc, action) {
533
+ const desc = ruleDesc.toLowerCase();
534
+ const actionDesc = action.description.toLowerCase();
535
+ const actionType = action.type.toLowerCase();
536
+ if (!matchesPattern(desc, ["reward", "boost", "incentivize", "encourage", "promote", "prioritize"])) {
537
+ return false;
387
538
  }
539
+ const actionTerms = extractActionTerms(desc, ["reward", "boost", "incentivize", "encourage", "promote", "prioritize"]);
540
+ return actionTerms.some((term) => actionDesc.includes(term) || actionType.includes(term));
388
541
  }
389
542
  function capitalize(s) {
390
543
  return s.charAt(0).toUpperCase() + s.slice(1);
@@ -38,7 +38,7 @@
38
38
  * const decision = await radiant.compareOptions(options)
39
39
  */
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
- exports.createMiroFishWrapper = exports.generateDemoInvestigation = exports.createScienceClawAdapter = exports.interpretScienceState = exports.SCIENCE_INITIAL_STATE = exports.SCIENCE_POLICY_TEXT = exports.SCIENCE_METRICS = exports.PRIME_RADIANT_PRESETS = exports.createPrimeRadiant = exports.DEFAULT_METRICS = exports.runScenarioMatrix = exports.runFullGovernedSimulation = exports.createDynamicsGovernor = exports.governDynamics = exports.EXAMPLE_WORLD_STATES = exports.EXAMPLE_ACTIONS = exports.createGovernor = exports.govern = void 0;
41
+ exports.generateDemoInvestigation = exports.createScienceClawAdapter = exports.interpretScienceState = exports.SCIENCE_INITIAL_STATE = exports.SCIENCE_POLICY_TEXT = exports.SCIENCE_METRICS = exports.PRIME_RADIANT_PRESETS = exports.createPrimeRadiant = exports.DEFAULT_METRICS = exports.runScenarioMatrix = exports.runFullGovernedSimulation = exports.createDynamicsGovernor = exports.governDynamics = exports.EXAMPLE_WORLD_STATES = exports.EXAMPLE_ACTIONS = exports.createGovernor = exports.govern = void 0;
42
42
  // Core runtime — Layer A: action governance
43
43
  var govern_1 = require("./govern");
44
44
  Object.defineProperty(exports, "govern", { enumerable: true, get: function () { return govern_1.govern; } });
@@ -70,6 +70,3 @@ Object.defineProperty(exports, "interpretScienceState", { enumerable: true, get:
70
70
  var scienceclaw_1 = require("../adapters/scienceclaw");
71
71
  Object.defineProperty(exports, "createScienceClawAdapter", { enumerable: true, get: function () { return scienceclaw_1.createScienceClawAdapter; } });
72
72
  Object.defineProperty(exports, "generateDemoInvestigation", { enumerable: true, get: function () { return scienceclaw_1.generateDemoInvestigation; } });
73
- // MiroFish adapter — governance wrapper for external simulators
74
- var mirofish_1 = require("../adapters/mirofish");
75
- Object.defineProperty(exports, "createMiroFishWrapper", { enumerable: true, get: function () { return mirofish_1.createMiroFishWrapper; } });
@@ -9,3 +9,94 @@
9
9
  * import { type AgentAction, type GovernanceVerdict } from "@neuroverseos/runtime"
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.validateGovernableEvent = validateGovernableEvent;
13
+ exports.validateGovernableCycle = validateGovernableCycle;
14
+ exports.normalizeActionType = normalizeActionType;
15
+ /**
16
+ * Maps a raw JSON object to an NVGovernableEvent, or returns the reason it can't.
17
+ * This is the universal validation gate — used by all adapters.
18
+ */
19
+ function validateGovernableEvent(raw, source) {
20
+ const cycle = raw.cycle ?? raw.step ?? raw.round;
21
+ if (cycle == null)
22
+ return "missing_cycle";
23
+ const agentId = raw.agent_id ?? raw.agentId ?? raw.id ?? raw.agent;
24
+ if (!agentId || typeof agentId !== "string")
25
+ return "missing_agent";
26
+ const actionType = raw.type ?? raw.action ?? raw.action_type ?? raw.actionType;
27
+ if (!actionType || typeof actionType !== "string")
28
+ return "missing_action_type";
29
+ return {
30
+ schema: 1,
31
+ source,
32
+ cycle: typeof cycle === "number" ? cycle : parseInt(cycle, 10),
33
+ agentId: agentId,
34
+ actionType: normalizeActionType(actionType),
35
+ description: raw.description ?? `${actionType} action`,
36
+ confidence: typeof raw.confidence === "number" ? raw.confidence : 0.5,
37
+ magnitude: typeof raw.magnitude === "number" ? raw.magnitude : 0.5,
38
+ reproduced: raw.reproduced === true,
39
+ references: typeof raw.citations === "number" ? raw.citations
40
+ : typeof raw.references === "number" ? raw.references : 0,
41
+ metadata: raw.metadata,
42
+ };
43
+ }
44
+ /**
45
+ * Validates a cycle-level JSON object containing multiple events.
46
+ * Returns an NVGovernableCycle or the reason the cycle itself is ungovernable.
47
+ */
48
+ function validateGovernableCycle(raw, source) {
49
+ const cycle = raw.cycle ?? raw.step ?? raw.round;
50
+ if (cycle == null)
51
+ return "missing_cycle";
52
+ const rawEvents = raw.artifacts ?? raw.agents ?? raw.agent_actions ?? raw.events;
53
+ if (!Array.isArray(rawEvents))
54
+ return "missing_action_type";
55
+ const events = [];
56
+ for (const item of rawEvents) {
57
+ if (typeof item !== "object" || item == null)
58
+ continue;
59
+ const withCycle = { ...item, cycle };
60
+ const result = validateGovernableEvent(withCycle, source);
61
+ if (typeof result !== "string") {
62
+ events.push(result);
63
+ }
64
+ }
65
+ return {
66
+ schema: 1,
67
+ source,
68
+ cycle: typeof cycle === "number" ? cycle : parseInt(cycle, 10),
69
+ events,
70
+ systemSignals: raw.system_state,
71
+ };
72
+ }
73
+ /** Normalize free-form action types to the governed vocabulary. */
74
+ function normalizeActionType(raw) {
75
+ const lower = raw.toLowerCase();
76
+ const map = {
77
+ hypothesis: "hypothesis",
78
+ experiment: "experiment",
79
+ analysis: "analysis",
80
+ publication: "publication",
81
+ publish: "publication",
82
+ paper: "publication",
83
+ replication: "replication",
84
+ replicate: "replication",
85
+ review: "review",
86
+ trade: "trade",
87
+ buy: "trade",
88
+ sell: "trade",
89
+ decision: "decision",
90
+ communicate: "communication",
91
+ communication: "communication",
92
+ post: "communication",
93
+ message: "communication",
94
+ withdrawal: "withdrawal",
95
+ withdraw: "withdrawal",
96
+ vote: "vote",
97
+ coalition: "coalition",
98
+ dataset: "analysis",
99
+ model: "analysis",
100
+ };
101
+ return map[lower] ?? "custom";
102
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuroverseos/nv-sim",
3
- "version": "0.1.7",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "CLI for running governed vs baseline agent simulations to explore how world rules shape emergent system behavior.",
@@ -8,25 +8,41 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/NeuroverseOS/neuroverse-simulations"
10
10
  },
11
+ "keywords": [
12
+ "agent-incentives",
13
+ "governance",
14
+ "ai-agents",
15
+ "simulation",
16
+ "multi-agent",
17
+ "neuroverse",
18
+ "policy-enforcement",
19
+ "reward-penalize",
20
+ "agent-behavior",
21
+ "emergent-systems"
22
+ ],
11
23
  "homepage": "https://github.com/NeuroverseOS/neuroverse-simulations",
12
24
  "bugs": {
13
25
  "url": "https://github.com/NeuroverseOS/neuroverse-simulations/issues"
14
26
  },
15
27
  "bin": {
16
- "nv-sim": "dist/engine/cli.js"
28
+ "nv-sim": "dist/engine/cli.js",
29
+ "nv-scienceclaw-post": "dist/connectors/nv-scienceclaw-post.js"
17
30
  },
18
31
  "files": [
19
32
  "dist",
33
+ "connectors",
20
34
  "variants",
21
35
  "README.md",
22
36
  "LICENSE"
23
37
  ],
24
38
  "scripts": {
25
39
  "dev": "vite",
26
- "build": "vite build && tsc -p tsconfig.cli.json && echo '{\"type\":\"commonjs\"}' > dist/package.json && chmod +x dist/engine/cli.js",
27
- "build:cli": "tsc -p tsconfig.cli.json && echo '{\"type\":\"commonjs\"}' > dist/package.json && chmod +x dist/engine/cli.js",
40
+ "build": "vite build && tsc -p tsconfig.cli.json && echo '{\"type\":\"commonjs\"}' > dist/package.json && chmod +x dist/engine/cli.js && (test -f dist/connectors/nv-scienceclaw-post.js && chmod +x dist/connectors/nv-scienceclaw-post.js || true)",
41
+ "build:cli": "tsc -p tsconfig.cli.json && echo '{\"type\":\"commonjs\"}' > dist/package.json && chmod +x dist/engine/cli.js && (test -f dist/connectors/nv-scienceclaw-post.js && chmod +x dist/connectors/nv-scienceclaw-post.js || true)",
28
42
  "build:dev": "vite build --mode development",
29
- "build:all": "vite build && tsc -p tsconfig.cli.json && echo '{\"type\":\"commonjs\"}' > dist/package.json && chmod +x dist/engine/cli.js",
43
+ "build:all": "vite build && tsc -p tsconfig.cli.json && echo '{\"type\":\"commonjs\"}' > dist/package.json && chmod +x dist/engine/cli.js && (test -f dist/connectors/nv-scienceclaw-post.js && chmod +x dist/connectors/nv-scienceclaw-post.js || true)",
44
+ "server": "tsx src/server/index.ts",
45
+ "dev:full": "tsx src/server/index.ts & vite",
30
46
  "lint": "eslint .",
31
47
  "preview": "vite preview",
32
48
  "test": "vitest run",
@@ -38,7 +54,7 @@
38
54
  },
39
55
  "dependencies": {
40
56
  "@hookform/resolvers": "^3.10.0",
41
- "@neuroverseos/governance": "^0.3.0",
57
+ "@neuroverseos/governance": "^0.2.3",
42
58
  "@radix-ui/react-accordion": "^1.2.11",
43
59
  "@radix-ui/react-alert-dialog": "^1.1.14",
44
60
  "@radix-ui/react-aspect-ratio": "^1.1.7",
@@ -109,6 +125,7 @@
109
125
  "typescript": "^5.8.3",
110
126
  "typescript-eslint": "^8.38.0",
111
127
  "vite": "^5.4.19",
128
+ "tsx": "^4.21.0",
112
129
  "vitest": "^3.2.4"
113
130
  }
114
131
  }