@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.
- package/README.md +375 -197
- package/connectors/nv_mirofish_wrapper.py +841 -0
- package/connectors/nv_scienceclaw_wrapper.py +453 -0
- package/dist/adapters/scienceclaw.js +52 -2
- package/dist/assets/index-B43_0HyO.css +1 -0
- package/dist/assets/index-CdghpsS8.js +595 -0
- package/dist/assets/{reportEngine-BVdQ2_nW.js → reportEngine-CYSZfooa.js} +1 -1
- package/dist/connectors/nv-scienceclaw-post.js +376 -0
- package/dist/engine/aiProvider.js +82 -3
- package/dist/engine/analyzer.js +12 -24
- package/dist/engine/chaosEngine.js +3 -9
- package/dist/engine/cli.js +123 -218
- package/dist/engine/dynamicsGovernance.js +4 -0
- package/dist/engine/fullGovernedLoop.js +16 -1
- package/dist/engine/goalEngine.js +3 -4
- package/dist/engine/governance.js +18 -0
- package/dist/engine/index.js +19 -29
- package/dist/engine/intentTranslator.js +281 -0
- package/dist/engine/liveAdapter.js +100 -18
- package/dist/engine/liveVisualizer.js +2656 -866
- package/dist/engine/narrativeInjection.js +78 -89
- package/dist/engine/policyEngine.js +171 -58
- package/dist/engine/primeRadiant.js +2 -8
- package/dist/engine/reasoningEngine.js +2 -7
- package/dist/engine/scenarioCapsule.js +77 -133
- package/dist/engine/scenarioLibrary.js +52 -131
- package/dist/engine/swarmSimulation.js +1 -9
- package/dist/engine/worldBridge.js +22 -8
- package/dist/engine/worldComparison.js +12 -25
- package/dist/index.html +2 -2
- package/dist/lib/reasoningEngine.js +17 -1
- package/dist/lib/simulationAdapter.js +11 -11
- package/dist/lib/swarmParser.js +1 -1
- package/dist/runtime/govern.js +160 -7
- package/dist/runtime/index.js +1 -4
- package/dist/runtime/types.js +91 -0
- package/package.json +23 -6
- package/dist/adapters/mirofish.js +0 -461
- package/dist/assets/index-CHmUN8s0.js +0 -532
- package/dist/assets/index-DWgMnB7I.css +0 -1
- package/dist/assets/mirotir-logo-DUexumBH.svg +0 -185
- 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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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:
|
|
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-
|
|
18
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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
|
-
|
|
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
|
-
* -
|
|
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 (
|
|
41
|
-
return "
|
|
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
|
|
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 "
|
|
111
|
-
return
|
|
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
|
-
// ──
|
|
125
|
-
function
|
|
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 (
|
|
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: "
|
|
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
|
-
|
|
680
|
+
agent_swarm: "Agent Swarm",
|
|
681
681
|
netlogo: "NetLogo",
|
|
682
682
|
mesa: "Mesa (Python)",
|
|
683
683
|
anylogic_csv: "AnyLogic / CSV",
|
package/dist/lib/swarmParser.js
CHANGED
|
@@ -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
|
-
*
|
|
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.
|
package/dist/runtime/govern.js
CHANGED
|
@@ -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
|
-
//
|
|
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: "
|
|
97
|
-
impactReduction:
|
|
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);
|
package/dist/runtime/index.js
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* const decision = await radiant.compareOptions(options)
|
|
39
39
|
*/
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
-
exports.
|
|
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; } });
|
package/dist/runtime/types.js
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
}
|