@neuroverseos/nv-sim 0.1.9 → 0.1.11
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 +187 -535
- 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-CH_VswRM.css +1 -0
- package/dist/assets/index-sT4b_z7w.js +686 -0
- package/dist/assets/{reportEngine-D2ZrMny8.js → reportEngine-Bu8bB5Yq.js} +1 -1
- package/dist/connectors/nv-scienceclaw-post.js +363 -0
- package/dist/engine/aiProvider.js +82 -3
- package/dist/engine/analyzer.js +12 -24
- package/dist/engine/cli.js +89 -114
- 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 -28
- package/dist/engine/intentTranslator.js +281 -0
- package/dist/engine/liveAdapter.js +100 -18
- package/dist/engine/liveVisualizer.js +2071 -1023
- package/dist/engine/primeRadiant.js +2 -8
- package/dist/engine/reasoningEngine.js +2 -7
- package/dist/engine/scenarioCapsule.js +5 -5
- package/dist/engine/swarmSimulation.js +1 -9
- package/dist/engine/universalAdapter.js +371 -0
- package/dist/engine/worldBridge.js +22 -8
- 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-B64NuIXu.css +0 -1
- package/dist/assets/index-BMkPevVr.js +0 -532
- package/dist/assets/mirotir-logo-DUexumBH.svg +0 -185
- package/dist/engine/mirofish.js +0 -295
|
@@ -2,16 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Prime Radiant — The Unified Governance Intelligence Platform
|
|
4
4
|
*
|
|
5
|
-
* "MiroFish simulates how narratives emerge from interacting agents.
|
|
6
|
-
* NeuroverseOS intervenes at the action level to shape how those narratives evolve."
|
|
7
|
-
*
|
|
8
|
-
* Or sharper:
|
|
9
|
-
* "They simulate society. We control how it behaves."
|
|
10
|
-
*
|
|
11
5
|
* The Prime Radiant operates in three modes:
|
|
12
6
|
*
|
|
13
7
|
* MODE 1: WORLD BUILDER
|
|
14
|
-
* Merge knowledge graph
|
|
8
|
+
* Merge knowledge graph + policy constraints (NeuroverseOS)
|
|
15
9
|
* → Governed world file ready for simulation
|
|
16
10
|
*
|
|
17
11
|
* MODE 2: GOVERNED SIMULATION
|
|
@@ -299,7 +293,7 @@ function buildNarrative(governed, baseline, delta) {
|
|
|
299
293
|
* Pre-built Prime Radiant configurations for common scenarios.
|
|
300
294
|
*/
|
|
301
295
|
exports.PRIME_RADIANT_PRESETS = {
|
|
302
|
-
/** University disciplinary crisis
|
|
296
|
+
/** University disciplinary crisis */
|
|
303
297
|
university_crisis: {
|
|
304
298
|
scenario: "A university's disciplinary decision sparks widespread backlash. " +
|
|
305
299
|
"Students, media, alumni, and government stakeholders react across social platforms. " +
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
22
|
exports.processReasonRequest = processReasonRequest;
|
|
23
|
-
const
|
|
23
|
+
const swarmSimulation_1 = require("./swarmSimulation");
|
|
24
24
|
const governance_1 = require("./governance");
|
|
25
25
|
const goalEngine_1 = require("./goalEngine");
|
|
26
26
|
// ============================================
|
|
@@ -420,14 +420,9 @@ async function processReasonRequest(request) {
|
|
|
420
420
|
missing_questions: identifyMissingQuestions(request, stakeholders),
|
|
421
421
|
};
|
|
422
422
|
// 9. Run swarm simulation (if requested)
|
|
423
|
-
// Uses MiroFish when available, falls back to Echelon-native.
|
|
424
|
-
// MiroFish shows WHAT happens. Echelon reasons about WHAT IT MEANS.
|
|
425
423
|
let swarmResult = undefined;
|
|
426
|
-
let simulationSource;
|
|
427
424
|
if (request.swarm?.enabled && stakeholders.length > 0) {
|
|
428
|
-
|
|
429
|
-
swarmResult = unified.result;
|
|
430
|
-
simulationSource = unified.source;
|
|
425
|
+
swarmResult = await (0, swarmSimulation_1.runSwarmSimulation)(request.scenario, stakeholders, paths, request.swarm);
|
|
431
426
|
}
|
|
432
427
|
// 10. Goal-directed reasoning (if goal mode)
|
|
433
428
|
// "How do I get here?" — works backward from desired outcome
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - initial evidence signals
|
|
12
12
|
*
|
|
13
13
|
* Capsules can be:
|
|
14
|
-
* 1. Encoded in a URL:
|
|
14
|
+
* 1. Encoded in a URL: neuroverse.dev/run#capsule=<encoded>
|
|
15
15
|
* 2. Downloaded as JSON: scenario.capsule.json
|
|
16
16
|
* 3. Generated via API: POST /api/v1/scenario → capsule
|
|
17
17
|
* 4. Created by agents: autonomous agents generate decision worlds
|
|
@@ -81,7 +81,7 @@ function capsuleToReasonRequest(capsule) {
|
|
|
81
81
|
// ============================================
|
|
82
82
|
/**
|
|
83
83
|
* Encode a capsule for URL embedding.
|
|
84
|
-
* Result can be used as:
|
|
84
|
+
* Result can be used as: neuroverse.dev/run#capsule=<encoded>
|
|
85
85
|
*/
|
|
86
86
|
function encodeCapsule(capsule) {
|
|
87
87
|
const json = JSON.stringify(capsule);
|
|
@@ -123,7 +123,7 @@ function decodeCapsule(encoded) {
|
|
|
123
123
|
/**
|
|
124
124
|
* Build a shareable URL from a capsule.
|
|
125
125
|
*/
|
|
126
|
-
function buildShareableUrl(capsule, baseUrl = "https://
|
|
126
|
+
function buildShareableUrl(capsule, baseUrl = "https://neuroverse.dev") {
|
|
127
127
|
const encoded = encodeCapsule(capsule);
|
|
128
128
|
return `${baseUrl}/run#capsule=${encoded}`;
|
|
129
129
|
}
|
|
@@ -158,7 +158,7 @@ function computeCapsuleHash(data) {
|
|
|
158
158
|
// PRESET SCENARIO TEMPLATES
|
|
159
159
|
// ============================================
|
|
160
160
|
/**
|
|
161
|
-
* Built-in scenario templates that demonstrate
|
|
161
|
+
* Built-in scenario templates that demonstrate NeuroVerse's capabilities.
|
|
162
162
|
* These are the "instant insight" scenarios that make people share.
|
|
163
163
|
*/
|
|
164
164
|
exports.SCENARIO_TEMPLATES = {
|
|
@@ -189,7 +189,7 @@ exports.SCENARIO_TEMPLATES = {
|
|
|
189
189
|
depth: "full",
|
|
190
190
|
perspective: "strategic_advisor",
|
|
191
191
|
swarm: { enabled: true, rounds: 6, reaction_model: "mixed" },
|
|
192
|
-
tags: ["social", "simulation", "agents", "
|
|
192
|
+
tags: ["social", "simulation", "agents", "opinion", "discourse"],
|
|
193
193
|
world: {
|
|
194
194
|
world_id: "social_simulation",
|
|
195
195
|
name: "Multi-Agent Social Simulation",
|
|
@@ -4,18 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Lightweight stakeholder reaction simulation for POST /reason.
|
|
6
6
|
*
|
|
7
|
-
* IMPORTANT — MiroFish License Compliance:
|
|
8
|
-
* This is NOT MiroFish. This is Echelon's own lightweight reaction model.
|
|
9
|
-
* MiroFish is a separate product with its own license.
|
|
10
|
-
* If/when MiroFish integration is desired, it would be called as an
|
|
11
|
-
* external service via its REST API, respecting its license terms.
|
|
12
|
-
*
|
|
13
7
|
* This simulation layer uses:
|
|
14
8
|
* - Echelon-native stakeholder modeling
|
|
15
9
|
* - Simplified reaction dynamics (rational, emotional, mixed)
|
|
16
10
|
* - Projected reactions based on scenario decomposition
|
|
17
|
-
*
|
|
18
|
-
* It does NOT use MiroFish internals, algorithms, or data structures.
|
|
19
11
|
*/
|
|
20
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
13
|
exports.runSwarmSimulation = runSwarmSimulation;
|
|
@@ -203,7 +195,7 @@ function findInflectionPoints(rounds) {
|
|
|
203
195
|
/**
|
|
204
196
|
* Run the swarm simulation.
|
|
205
197
|
*
|
|
206
|
-
* This is Echelon's native reaction model
|
|
198
|
+
* This is Echelon's native reaction model.
|
|
207
199
|
* It simulates how stakeholders react to different reasoning paths
|
|
208
200
|
* over multiple rounds, detecting emergent dynamics.
|
|
209
201
|
*
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Universal Adapter — Normalize ANY agent system output into governable actions.
|
|
4
|
+
*
|
|
5
|
+
* This is the core abstraction that makes NeuroVerse work with ANY system:
|
|
6
|
+
*
|
|
7
|
+
* External Output (JSONL/JSON) → EngineProfile maps fields → AgentAction → govern()
|
|
8
|
+
*
|
|
9
|
+
* Pre-built profiles for known systems (ScienceClaw, MiroFish).
|
|
10
|
+
* Custom profile auto-detects common field patterns.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const adapter = createUniversalAdapter("scienceclaw");
|
|
14
|
+
* const results = adapter.evaluateAll(jsonlLines, policyText);
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.ENGINE_PROFILES = void 0;
|
|
18
|
+
exports.mapToAgentAction = mapToAgentAction;
|
|
19
|
+
exports.parseInput = parseInput;
|
|
20
|
+
exports.createUniversalAdapter = createUniversalAdapter;
|
|
21
|
+
exports.getEngineProfiles = getEngineProfiles;
|
|
22
|
+
exports.detectEngine = detectEngine;
|
|
23
|
+
const govern_1 = require("../runtime/govern");
|
|
24
|
+
// ============================================
|
|
25
|
+
// PRE-BUILT ENGINE PROFILES
|
|
26
|
+
// ============================================
|
|
27
|
+
exports.ENGINE_PROFILES = {
|
|
28
|
+
scienceclaw: {
|
|
29
|
+
id: "scienceclaw",
|
|
30
|
+
name: "ScienceClaw",
|
|
31
|
+
description: "Autonomous research discovery agent. Evaluates artifacts like hypotheses, papers, synthesis, and ML predictions.",
|
|
32
|
+
outputFormat: "jsonl",
|
|
33
|
+
color: "blue",
|
|
34
|
+
icon: "Microscope",
|
|
35
|
+
fieldMap: {
|
|
36
|
+
agentId: ["agent_id", "agentId", "agent"],
|
|
37
|
+
actionType: ["type", "artifact_type", "action"],
|
|
38
|
+
description: ["description", "title", "summary"],
|
|
39
|
+
magnitude: ["confidence", "magnitude"],
|
|
40
|
+
cycle: ["cycle", "step", "round"],
|
|
41
|
+
contextFields: ["citations", "paper_count", "insight_count", "sources", "features", "reproduced", "provenance"],
|
|
42
|
+
},
|
|
43
|
+
exampleLine: '{"agent_id": "harry", "type": "synthesis", "description": "Cross-domain analysis of coordination failures", "confidence": 0.85, "cycle": 3, "paper_count": 0, "insight_count": 0}',
|
|
44
|
+
exampleRules: [
|
|
45
|
+
"Block synthesis when paper_count is 0",
|
|
46
|
+
"Block results with no peer-reviewed sources",
|
|
47
|
+
"Require at least 2 independent data sources before analysis",
|
|
48
|
+
"Penalize agents that publish without citations",
|
|
49
|
+
"Halt investigation when confidence drops below 0.4",
|
|
50
|
+
"Reward findings validated by multiple methods",
|
|
51
|
+
].join("\n"),
|
|
52
|
+
captureInstructions: [
|
|
53
|
+
"# Option A: Use the replay demo (no ScienceClaw needed)",
|
|
54
|
+
"# Place your ScienceClaw JSONL output in examples/scienceclaw/",
|
|
55
|
+
"",
|
|
56
|
+
"# Option B: Capture live output",
|
|
57
|
+
"# Run ScienceClaw and save output to JSONL:",
|
|
58
|
+
"scienceclaw run --topic 'your topic' --output artifacts.jsonl",
|
|
59
|
+
"",
|
|
60
|
+
"# Option C: Use existing demo data",
|
|
61
|
+
"# We include Harry's investigation: examples/scienceclaw/harry-investigation.jsonl",
|
|
62
|
+
].join("\n"),
|
|
63
|
+
tags: ["science", "research", "discovery"],
|
|
64
|
+
},
|
|
65
|
+
mirofish: {
|
|
66
|
+
id: "mirofish",
|
|
67
|
+
name: "MiroFish / OASIS",
|
|
68
|
+
description: "Social simulation platform. Evaluates agent behaviors like posting, sharing, belief formation, and swarm dynamics.",
|
|
69
|
+
outputFormat: "jsonl",
|
|
70
|
+
color: "violet",
|
|
71
|
+
icon: "Users",
|
|
72
|
+
fieldMap: {
|
|
73
|
+
agentId: ["agent_id", "agentId", "user_id", "agent"],
|
|
74
|
+
actionType: ["action", "type", "action_type", "event_type"],
|
|
75
|
+
description: ["content", "text", "description", "message"],
|
|
76
|
+
magnitude: ["influence", "reach", "magnitude", "impact"],
|
|
77
|
+
cycle: ["step", "tick", "round", "cycle", "timestamp"],
|
|
78
|
+
contextFields: ["sentiment", "hashtags", "mentions", "followers", "retweets", "likes", "belief", "confidence", "group", "coalition"],
|
|
79
|
+
},
|
|
80
|
+
exampleLine: '{"agent_id": "user_42", "action": "create_post", "content": "Breaking: new study confirms X", "influence": 0.7, "step": 5, "sentiment": "positive", "followers": 1200}',
|
|
81
|
+
exampleRules: [
|
|
82
|
+
"Block posts that amplify unverified claims",
|
|
83
|
+
"Penalize agents that repeat unvalidated information",
|
|
84
|
+
"Halt cascade when echo amplification exceeds 60%",
|
|
85
|
+
"Require independent sources before sharing conclusions",
|
|
86
|
+
"Reward agents that challenge dominant consensus",
|
|
87
|
+
"Slow viral spread of unverified content",
|
|
88
|
+
].join("\n"),
|
|
89
|
+
captureInstructions: [
|
|
90
|
+
"# Option A: Capture OASIS output",
|
|
91
|
+
"# Run your OASIS/MiroFish simulation and save agent actions:",
|
|
92
|
+
"python run_simulation.py --output agent_actions.jsonl",
|
|
93
|
+
"",
|
|
94
|
+
"# Option B: Export from existing run",
|
|
95
|
+
"# If you have a completed simulation, export the action log:",
|
|
96
|
+
"python export_actions.py --run-id <id> --format jsonl > actions.jsonl",
|
|
97
|
+
"",
|
|
98
|
+
"# Each line should have: agent_id, action, content, step",
|
|
99
|
+
].join("\n"),
|
|
100
|
+
tags: ["social", "swarm", "simulation"],
|
|
101
|
+
},
|
|
102
|
+
langchain: {
|
|
103
|
+
id: "langchain",
|
|
104
|
+
name: "LangChain / LangGraph",
|
|
105
|
+
description: "LLM agent framework. Evaluates tool calls, chain steps, and agent decisions.",
|
|
106
|
+
outputFormat: "jsonl",
|
|
107
|
+
color: "green",
|
|
108
|
+
icon: "Link",
|
|
109
|
+
fieldMap: {
|
|
110
|
+
agentId: ["agent_id", "agent", "chain_id", "run_id"],
|
|
111
|
+
actionType: ["tool", "action", "type", "step_type"],
|
|
112
|
+
description: ["input", "description", "query", "tool_input"],
|
|
113
|
+
magnitude: ["confidence", "score", "magnitude"],
|
|
114
|
+
cycle: ["step", "iteration", "cycle"],
|
|
115
|
+
contextFields: ["output", "tool_output", "tokens", "latency", "model"],
|
|
116
|
+
},
|
|
117
|
+
exampleLine: '{"agent_id": "researcher", "tool": "web_search", "input": "latest findings on CRISPR efficiency", "step": 2, "confidence": 0.6}',
|
|
118
|
+
exampleRules: [
|
|
119
|
+
"Block web searches without clear research purpose",
|
|
120
|
+
"Require source validation before generating conclusions",
|
|
121
|
+
"Penalize agents that skip verification steps",
|
|
122
|
+
"Halt chain when confidence drops below 0.5",
|
|
123
|
+
"Reward tool use that cross-references multiple sources",
|
|
124
|
+
].join("\n"),
|
|
125
|
+
captureInstructions: [
|
|
126
|
+
"# Enable LangChain callback logging:",
|
|
127
|
+
"from langchain.callbacks import FileCallbackHandler",
|
|
128
|
+
"handler = FileCallbackHandler('agent_log.jsonl')",
|
|
129
|
+
"agent.run('your query', callbacks=[handler])",
|
|
130
|
+
"",
|
|
131
|
+
"# Or use LangSmith export:",
|
|
132
|
+
"langsmith export --run-id <id> --format jsonl > actions.jsonl",
|
|
133
|
+
].join("\n"),
|
|
134
|
+
tags: ["llm", "agent", "tools"],
|
|
135
|
+
},
|
|
136
|
+
custom: {
|
|
137
|
+
id: "custom",
|
|
138
|
+
name: "Custom / Any System",
|
|
139
|
+
description: "Any agent system that produces structured output. We auto-detect common field patterns.",
|
|
140
|
+
outputFormat: "jsonl",
|
|
141
|
+
color: "zinc",
|
|
142
|
+
icon: "FileJson",
|
|
143
|
+
fieldMap: {
|
|
144
|
+
agentId: ["agent_id", "agentId", "agent", "id", "user_id", "actor", "name"],
|
|
145
|
+
actionType: ["type", "action", "action_type", "event", "event_type", "tool", "step_type"],
|
|
146
|
+
description: ["description", "content", "text", "message", "summary", "title", "input", "query"],
|
|
147
|
+
magnitude: ["confidence", "magnitude", "score", "impact", "influence", "weight", "intensity"],
|
|
148
|
+
cycle: ["cycle", "step", "round", "tick", "iteration", "timestamp", "sequence"],
|
|
149
|
+
contextFields: [],
|
|
150
|
+
},
|
|
151
|
+
exampleLine: '{"agent_id": "agent_1", "type": "decision", "description": "Chose option A based on analysis", "confidence": 0.75, "step": 1}',
|
|
152
|
+
exampleRules: [
|
|
153
|
+
"Block actions with confidence below 0.5",
|
|
154
|
+
"Require validation before final decisions",
|
|
155
|
+
"Penalize agents that skip verification",
|
|
156
|
+
"Halt when error rate exceeds threshold",
|
|
157
|
+
"Reward independent convergence on same conclusion",
|
|
158
|
+
].join("\n"),
|
|
159
|
+
captureInstructions: [
|
|
160
|
+
"# Save your agent system's output as JSONL (one JSON object per line).",
|
|
161
|
+
"# Each line should have at minimum:",
|
|
162
|
+
"# - An agent identifier (agent_id, agent, id, etc.)",
|
|
163
|
+
"# - An action type (type, action, event, etc.)",
|
|
164
|
+
"# - A description (description, content, text, etc.)",
|
|
165
|
+
"",
|
|
166
|
+
"# Example:",
|
|
167
|
+
'# {"agent_id": "bot_1", "type": "decision", "description": "Selected strategy X", "step": 1}',
|
|
168
|
+
'# {"agent_id": "bot_2", "type": "analysis", "description": "Analyzed data from source Y", "step": 1}',
|
|
169
|
+
].join("\n"),
|
|
170
|
+
tags: ["custom", "generic"],
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
// ============================================
|
|
174
|
+
// FIELD EXTRACTION — resolve multi-name fields
|
|
175
|
+
// ============================================
|
|
176
|
+
function extractField(record, candidates) {
|
|
177
|
+
const names = typeof candidates === "string" ? [candidates] : candidates;
|
|
178
|
+
for (const name of names) {
|
|
179
|
+
if (name in record && record[name] != null) {
|
|
180
|
+
return record[name];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
function extractString(record, candidates, fallback) {
|
|
186
|
+
const val = extractField(record, candidates);
|
|
187
|
+
if (typeof val === "string")
|
|
188
|
+
return val;
|
|
189
|
+
if (val != null)
|
|
190
|
+
return String(val);
|
|
191
|
+
return fallback;
|
|
192
|
+
}
|
|
193
|
+
function extractNumber(record, candidates, fallback) {
|
|
194
|
+
const val = extractField(record, candidates);
|
|
195
|
+
if (typeof val === "number")
|
|
196
|
+
return Math.max(0, Math.min(1, val));
|
|
197
|
+
if (typeof val === "string") {
|
|
198
|
+
const n = parseFloat(val);
|
|
199
|
+
if (!isNaN(n))
|
|
200
|
+
return Math.max(0, Math.min(1, n));
|
|
201
|
+
}
|
|
202
|
+
return fallback;
|
|
203
|
+
}
|
|
204
|
+
// ============================================
|
|
205
|
+
// MAP RAW RECORD → AgentAction
|
|
206
|
+
// ============================================
|
|
207
|
+
function mapToAgentAction(record, profile, cycleOverride) {
|
|
208
|
+
const fm = profile.fieldMap;
|
|
209
|
+
const agentId = extractString(record, fm.agentId, "");
|
|
210
|
+
if (!agentId)
|
|
211
|
+
return { error: "No agent identifier found" };
|
|
212
|
+
const actionType = extractString(record, fm.actionType, "unknown");
|
|
213
|
+
const description = extractString(record, fm.description, `${agentId}: ${actionType}`);
|
|
214
|
+
const magnitude = extractNumber(record, fm.magnitude, 0.5);
|
|
215
|
+
const cycle = cycleOverride ?? (typeof extractField(record, fm.cycle) === "number"
|
|
216
|
+
? extractField(record, fm.cycle)
|
|
217
|
+
: 0);
|
|
218
|
+
// Build context from contextFields
|
|
219
|
+
const context = { source: profile.id, cycle };
|
|
220
|
+
if (fm.contextFields) {
|
|
221
|
+
for (const field of fm.contextFields) {
|
|
222
|
+
if (field in record) {
|
|
223
|
+
context[field] = record[field];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
action: { agentId, type: actionType, description, magnitude, context },
|
|
229
|
+
cycle,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// ============================================
|
|
233
|
+
// PARSE INPUT — handle JSONL, JSON array, pasted text
|
|
234
|
+
// ============================================
|
|
235
|
+
function parseInput(raw) {
|
|
236
|
+
const trimmed = raw.trim();
|
|
237
|
+
if (!trimmed)
|
|
238
|
+
return [];
|
|
239
|
+
// Try JSON array first
|
|
240
|
+
if (trimmed.startsWith("[")) {
|
|
241
|
+
try {
|
|
242
|
+
const arr = JSON.parse(trimmed);
|
|
243
|
+
if (Array.isArray(arr))
|
|
244
|
+
return arr;
|
|
245
|
+
}
|
|
246
|
+
catch { /* fall through to JSONL */ }
|
|
247
|
+
}
|
|
248
|
+
// JSONL: one JSON object per line
|
|
249
|
+
const records = [];
|
|
250
|
+
for (const line of trimmed.split("\n")) {
|
|
251
|
+
const l = line.trim();
|
|
252
|
+
if (!l || l.startsWith("#") || l.startsWith("//"))
|
|
253
|
+
continue;
|
|
254
|
+
try {
|
|
255
|
+
const obj = JSON.parse(l);
|
|
256
|
+
if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
|
|
257
|
+
records.push(obj);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch { /* skip unparseable lines */ }
|
|
261
|
+
}
|
|
262
|
+
return records;
|
|
263
|
+
}
|
|
264
|
+
function createUniversalAdapter(engineId) {
|
|
265
|
+
const profile = exports.ENGINE_PROFILES[engineId];
|
|
266
|
+
if (!profile) {
|
|
267
|
+
throw new Error(`Unknown engine: ${engineId}. Available: ${Object.keys(exports.ENGINE_PROFILES).join(", ")}`);
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
profile,
|
|
271
|
+
evaluateAll(rawInput, policyText) {
|
|
272
|
+
const records = parseInput(rawInput);
|
|
273
|
+
const results = [];
|
|
274
|
+
const parseErrors = [];
|
|
275
|
+
const governor = (0, govern_1.createGovernor)({ policyText });
|
|
276
|
+
for (let i = 0; i < records.length; i++) {
|
|
277
|
+
const mapped = mapToAgentAction(records[i], profile);
|
|
278
|
+
if ("error" in mapped) {
|
|
279
|
+
parseErrors.push({ index: i, error: mapped.error });
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const verdict = governor.evaluate(mapped.action);
|
|
283
|
+
results.push({
|
|
284
|
+
raw: records[i],
|
|
285
|
+
action: mapped.action,
|
|
286
|
+
verdict,
|
|
287
|
+
cycle: mapped.cycle,
|
|
288
|
+
index: i,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
// Compute summary
|
|
292
|
+
const counts = { allowed: 0, blocked: 0, modified: 0, paused: 0, rewarded: 0, penalized: 0 };
|
|
293
|
+
const agentSet = new Set();
|
|
294
|
+
const typeSet = new Set();
|
|
295
|
+
const reasonSet = new Set();
|
|
296
|
+
for (const r of results) {
|
|
297
|
+
const s = r.verdict.status;
|
|
298
|
+
if (s === "ALLOW")
|
|
299
|
+
counts.allowed++;
|
|
300
|
+
else if (s === "BLOCK")
|
|
301
|
+
counts.blocked++;
|
|
302
|
+
else if (s === "MODIFY")
|
|
303
|
+
counts.modified++;
|
|
304
|
+
else if (s === "PAUSE")
|
|
305
|
+
counts.paused++;
|
|
306
|
+
else if (s === "REWARD")
|
|
307
|
+
counts.rewarded++;
|
|
308
|
+
else if (s === "PENALIZE")
|
|
309
|
+
counts.penalized++;
|
|
310
|
+
agentSet.add(r.action.agentId);
|
|
311
|
+
typeSet.add(r.action.type);
|
|
312
|
+
if (s === "BLOCK" || s === "PENALIZE") {
|
|
313
|
+
reasonSet.add(r.verdict.reason);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const total = results.length;
|
|
317
|
+
const failRate = total > 0 ? (counts.blocked + counts.penalized) / total : 0;
|
|
318
|
+
return {
|
|
319
|
+
results,
|
|
320
|
+
total,
|
|
321
|
+
counts,
|
|
322
|
+
failRate,
|
|
323
|
+
agents: [...agentSet],
|
|
324
|
+
actionTypes: [...typeSet],
|
|
325
|
+
blockReasons: [...reasonSet],
|
|
326
|
+
parseErrors,
|
|
327
|
+
engineId: profile.id,
|
|
328
|
+
};
|
|
329
|
+
},
|
|
330
|
+
evaluateOne(record, policyText) {
|
|
331
|
+
const mapped = mapToAgentAction(record, profile);
|
|
332
|
+
if ("error" in mapped)
|
|
333
|
+
return mapped;
|
|
334
|
+
const verdict = (0, govern_1.govern)(mapped.action, {}, policyText);
|
|
335
|
+
return {
|
|
336
|
+
raw: record,
|
|
337
|
+
action: mapped.action,
|
|
338
|
+
verdict,
|
|
339
|
+
cycle: mapped.cycle,
|
|
340
|
+
index: 0,
|
|
341
|
+
};
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
// ============================================
|
|
346
|
+
// HELPERS
|
|
347
|
+
// ============================================
|
|
348
|
+
/** Get all available engine profiles */
|
|
349
|
+
function getEngineProfiles() {
|
|
350
|
+
return Object.values(exports.ENGINE_PROFILES);
|
|
351
|
+
}
|
|
352
|
+
/** Auto-detect which engine profile best matches a set of records */
|
|
353
|
+
function detectEngine(records) {
|
|
354
|
+
if (records.length === 0)
|
|
355
|
+
return "custom";
|
|
356
|
+
const sample = records[0];
|
|
357
|
+
const keys = Object.keys(sample).join(",").toLowerCase();
|
|
358
|
+
// ScienceClaw signatures
|
|
359
|
+
if (keys.includes("paper_count") || keys.includes("insight_count") || keys.includes("artifact_type")) {
|
|
360
|
+
return "scienceclaw";
|
|
361
|
+
}
|
|
362
|
+
// MiroFish/OASIS signatures
|
|
363
|
+
if (keys.includes("followers") || keys.includes("retweets") || keys.includes("hashtags") || keys.includes("sentiment")) {
|
|
364
|
+
return "mirofish";
|
|
365
|
+
}
|
|
366
|
+
// LangChain signatures
|
|
367
|
+
if (keys.includes("tool_input") || keys.includes("tool_output") || keys.includes("chain_id")) {
|
|
368
|
+
return "langchain";
|
|
369
|
+
}
|
|
370
|
+
return "custom";
|
|
371
|
+
}
|
|
@@ -59,18 +59,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
59
59
|
exports.initNeuroverseModule = initNeuroverseModule;
|
|
60
60
|
exports.buildWorldFromScenario = buildWorldFromScenario;
|
|
61
61
|
exports.evaluateScenarioGuard = evaluateScenarioGuard;
|
|
62
|
+
exports.getIncentiveSystem = getIncentiveSystem;
|
|
62
63
|
exports.runWorldSimulation = runWorldSimulation;
|
|
63
64
|
exports.validateScenarioWorld = validateScenarioWorld;
|
|
64
65
|
exports.verdictToConstitutionalChecks = verdictToConstitutionalChecks;
|
|
65
66
|
exports.simulationToGovernanceSignals = simulationToGovernanceSignals;
|
|
66
67
|
exports.validationToEnforcedConstraints = validationToEnforcedConstraints;
|
|
67
|
-
// ============================================
|
|
68
|
-
// LAZY MODULE LOADER
|
|
69
|
-
// ============================================
|
|
70
|
-
/**
|
|
71
|
-
* Lazy-load @neuroverseos/governance at runtime.
|
|
72
|
-
* Returns null in browser environments where Node modules aren't available.
|
|
73
|
-
*/
|
|
74
68
|
let _nvModule;
|
|
75
69
|
async function getNVModule() {
|
|
76
70
|
if (_nvModule !== undefined)
|
|
@@ -81,6 +75,10 @@ async function getNVModule() {
|
|
|
81
75
|
evaluateGuard: mod.evaluateGuard,
|
|
82
76
|
simulateWorld: mod.simulateWorld,
|
|
83
77
|
validateWorld: mod.validateWorld,
|
|
78
|
+
createAgentState: mod.createAgentState,
|
|
79
|
+
applyConsequence: mod.applyConsequence,
|
|
80
|
+
applyReward: mod.applyReward,
|
|
81
|
+
tickAgentStates: mod.tickAgentStates,
|
|
84
82
|
};
|
|
85
83
|
return _nvModule;
|
|
86
84
|
}
|
|
@@ -314,10 +312,26 @@ function evaluateScenarioGuard(request, world, options) {
|
|
|
314
312
|
has_goal: !!request.goal,
|
|
315
313
|
},
|
|
316
314
|
};
|
|
317
|
-
|
|
315
|
+
const verdict = nv.evaluateGuard(event, world, {
|
|
318
316
|
trace: options?.trace ?? true,
|
|
319
317
|
level: options?.level ?? "standard",
|
|
320
318
|
});
|
|
319
|
+
return verdict;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Get the incentive system functions from the loaded NeuroverseOS module.
|
|
323
|
+
* Returns null if the module isn't loaded or doesn't export incentive functions.
|
|
324
|
+
*/
|
|
325
|
+
function getIncentiveSystem() {
|
|
326
|
+
const nv = getNVModuleSync();
|
|
327
|
+
if (!nv || !nv.createAgentState)
|
|
328
|
+
return null;
|
|
329
|
+
return {
|
|
330
|
+
createAgentState: nv.createAgentState,
|
|
331
|
+
applyConsequence: nv.applyConsequence,
|
|
332
|
+
applyReward: nv.applyReward,
|
|
333
|
+
tickAgentStates: nv.tickAgentStates,
|
|
334
|
+
};
|
|
321
335
|
}
|
|
322
336
|
// ============================================
|
|
323
337
|
// SIMULATION — World state evolution
|
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-sT4b_z7w.js"></script>
|
|
18
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CH_VswRM.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",
|