@neuroverseos/governance 0.3.0 → 0.3.3
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/.well-known/ai-plugin.json +34 -9
- package/AGENTS.md +72 -24
- package/README.md +352 -237
- package/dist/adapters/autoresearch.cjs +1152 -3
- package/dist/adapters/autoresearch.d.cts +11 -3
- package/dist/adapters/autoresearch.d.ts +11 -3
- package/dist/adapters/autoresearch.js +9 -4
- package/dist/adapters/deep-agents.cjs +1528 -0
- package/dist/adapters/deep-agents.d.cts +181 -0
- package/dist/adapters/deep-agents.d.ts +181 -0
- package/dist/adapters/deep-agents.js +17 -0
- package/dist/adapters/express.cjs +171 -32
- package/dist/adapters/express.d.cts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +5 -5
- package/dist/adapters/index.cjs +564 -121
- package/dist/adapters/index.d.cts +3 -1
- package/dist/adapters/index.d.ts +3 -1
- package/dist/adapters/index.js +38 -16
- package/dist/adapters/langchain.cjs +217 -57
- package/dist/adapters/langchain.d.cts +5 -5
- package/dist/adapters/langchain.d.ts +5 -5
- package/dist/adapters/langchain.js +6 -5
- package/dist/adapters/openai.cjs +219 -59
- package/dist/adapters/openai.d.cts +5 -5
- package/dist/adapters/openai.d.ts +5 -5
- package/dist/adapters/openai.js +6 -5
- package/dist/adapters/openclaw.cjs +217 -57
- package/dist/adapters/openclaw.d.cts +6 -6
- package/dist/adapters/openclaw.d.ts +6 -6
- package/dist/adapters/openclaw.js +6 -5
- package/dist/add-ROOZLU62.js +314 -0
- package/dist/behavioral-MJO34S6Q.js +118 -0
- package/dist/{bootstrap-GXVDZNF7.js → bootstrap-CQRZVOXK.js} +6 -4
- package/dist/bootstrap-emitter-Q7UIJZ2O.js +7 -0
- package/dist/bootstrap-parser-EEF36XDU.js +7 -0
- package/dist/browser.global.js +941 -0
- package/dist/{build-P42YFKQV.js → build-QKOBBC23.js} +7 -5
- package/dist/{chunk-COT5XS4V.js → chunk-3WQLXYTP.js} +17 -35
- package/dist/{chunk-ER62HNGF.js → chunk-4FLICVVA.js} +17 -37
- package/dist/chunk-5TPFNWRU.js +215 -0
- package/dist/chunk-5U2MQO5P.js +57 -0
- package/dist/{chunk-NF5POFCI.js → chunk-6S5CFQXY.js} +6 -4
- package/dist/{chunk-QPASI2BR.js → chunk-A7GKPPU7.js} +49 -10
- package/dist/{chunk-OGL7QXZS.js → chunk-B6OXJLJ5.js} +17 -3
- package/dist/{chunk-2PQU3VAN.js → chunk-BNKJPUPQ.js} +17 -35
- package/dist/chunk-BQZMOEML.js +43 -0
- package/dist/chunk-CNSO6XW5.js +207 -0
- package/dist/{chunk-JZPQGIKR.js → chunk-CTZHONLA.js} +65 -9
- package/dist/chunk-D2UCV5AK.js +326 -0
- package/dist/{chunk-XPDMYECO.js → chunk-EMQDLDAF.js} +1 -185
- package/dist/{chunk-GR6DGCZ2.js → chunk-F66BVUYB.js} +3 -3
- package/dist/{chunk-2NICNKOM.js → chunk-G7DJ6VOD.js} +5 -4
- package/dist/{chunk-4A7LISES.js → chunk-IS4WUH6Y.js} +45 -6
- package/dist/{chunk-MWDQ4MJB.js → chunk-MH7BT4VH.js} +5 -1
- package/dist/chunk-O5ABKEA7.js +304 -0
- package/dist/chunk-PVTQQS3Y.js +186 -0
- package/dist/{chunk-4QXB6PEO.js → chunk-QLPTHTVB.js} +37 -16
- package/dist/chunk-QWGCMQQD.js +16 -0
- package/dist/{chunk-T5EUJQE5.js → chunk-QXBFT7NI.js} +31 -2
- package/dist/{chunk-PDOZHZWL.js → chunk-TG6SEF24.js} +25 -4
- package/dist/chunk-U6U7EJZL.js +177 -0
- package/dist/{chunk-4JRYGIO7.js → chunk-W7LLXRGY.js} +110 -7
- package/dist/{chunk-BUWWN2NX.js → chunk-ZJTDUCC2.js} +9 -7
- package/dist/{chunk-FYS2CBUW.js → chunk-ZWI3NIXK.js} +10 -0
- package/dist/cli/neuroverse.cjs +5091 -2348
- package/dist/cli/neuroverse.js +52 -21
- package/dist/cli/plan.cjs +881 -41
- package/dist/cli/plan.js +7 -15
- package/dist/cli/run.cjs +289 -34
- package/dist/cli/run.js +4 -4
- package/dist/{configure-ai-TK67ZWZL.js → configure-ai-6TZ3MCSI.js} +1 -1
- package/dist/decision-flow-M63D47LO.js +61 -0
- package/dist/demo-G43RLCPK.js +469 -0
- package/dist/{derive-TLIV4OOU.js → derive-FJZVIPUZ.js} +5 -4
- package/dist/{doctor-XPDLEYXN.js → doctor-6BC6X2VO.js} +6 -4
- package/dist/equity-penalties-SG5IZQ7I.js +244 -0
- package/dist/{explain-IDCRWMPX.js → explain-RHBU2GBR.js} +6 -25
- package/dist/{guard-RV65TT4L.js → guard-AJCCGZMF.js} +8 -12
- package/dist/{guard-contract-WZx__PmU.d.cts → guard-contract-DqFcTScd.d.cts} +117 -5
- package/dist/{guard-contract-WZx__PmU.d.ts → guard-contract-DqFcTScd.d.ts} +117 -5
- package/dist/{guard-engine-JLTUARGU.js → guard-engine-PNR6MHCM.js} +3 -3
- package/dist/{impact-XPECYRLH.js → impact-3XVDSCBU.js} +5 -5
- package/dist/{improve-GPUBKTEA.js → improve-TQP4ECSY.js} +7 -26
- package/dist/index.cjs +5597 -4279
- package/dist/index.d.cts +597 -18
- package/dist/index.d.ts +597 -18
- package/dist/index.js +134 -41
- package/dist/{infer-world-7GVZWFX4.js → infer-world-IFXCACJ5.js} +1 -1
- package/dist/{init-PKPIYHYE.js → init-FYPV4SST.js} +1 -1
- package/dist/{init-world-VWMQZQC7.js → init-world-TI7ARHBT.js} +1 -1
- package/dist/mcp-server-5Y3ZM7TV.js +13 -0
- package/dist/{model-adapter-BB7G4MFI.js → model-adapter-VXEKB4LS.js} +1 -1
- package/dist/{playground-E664U4T6.js → playground-VZBNPPBO.js} +29 -19
- package/dist/{redteam-Z7WREJ44.js → redteam-MZPZD3EF.js} +4 -4
- package/dist/session-JYOARW54.js +15 -0
- package/dist/shared-7RLUHNMU.js +16 -0
- package/dist/shared-B8dvUUD8.d.cts +60 -0
- package/dist/shared-Dr5Wiay8.d.ts +60 -0
- package/dist/{simulate-VDOYQFRO.js → simulate-LJXYBC6M.js} +8 -33
- package/dist/{test-OGXJK4QU.js → test-BOOR4A5F.js} +4 -4
- package/dist/{trace-JVF67VR3.js → trace-PKV4KX56.js} +4 -4
- package/dist/{validate-LLBWVPGV.js → validate-RALX7CZS.js} +2 -2
- package/dist/{validate-engine-UIABSIHD.js → validate-engine-7ZXFVGF2.js} +1 -1
- package/dist/viz/assets/index-B8SaeJZZ.js +23 -0
- package/dist/viz/index.html +23 -0
- package/dist/{world-LAXO6DOX.js → world-BIP4GZBZ.js} +9 -11
- package/dist/world-loader-Y6HMQH2D.js +13 -0
- package/dist/worlds/coding-agent.nv-world.md +211 -0
- package/dist/worlds/research-agent.nv-world.md +169 -0
- package/dist/worlds/social-media.nv-world.md +198 -0
- package/dist/worlds/trading-agent.nv-world.md +218 -0
- package/examples/social-media-sim/bridge.py +209 -0
- package/examples/social-media-sim/simulation.py +927 -0
- package/package.json +30 -4
- package/policies/content-moderation-rules.txt +8 -0
- package/policies/marketing-rules.txt +8 -0
- package/policies/science-research-rules.txt +11 -0
- package/policies/social-media-rules.txt +7 -0
- package/policies/strict-rules.txt +8 -0
- package/policies/trading-rules.txt +8 -0
- package/simulate.html +1567 -0
- package/dist/chunk-YZFATT7X.js +0 -9
- package/dist/mcp-server-FPVSU32Z.js +0 -13
- package/dist/session-EKTRSR7C.js +0 -14
- package/dist/world-loader-HMPTOEA2.js +0 -9
package/dist/cli/plan.cjs
CHANGED
|
@@ -30,17 +30,27 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
-
// src/engine/
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
// src/engine/text-utils.ts
|
|
34
|
+
function normalizeEventText(event) {
|
|
35
|
+
return [
|
|
36
|
+
event.intent,
|
|
37
|
+
event.tool ?? "",
|
|
38
|
+
event.scope ?? ""
|
|
39
39
|
].join(" ").toLowerCase();
|
|
40
|
-
|
|
40
|
+
}
|
|
41
|
+
function extractKeywords(text, minLength = 3) {
|
|
42
|
+
return text.toLowerCase().split(/\s+/).filter((w) => w.length > minLength);
|
|
43
|
+
}
|
|
44
|
+
function matchesAllKeywords(eventText, ruleText) {
|
|
45
|
+
const keywords = extractKeywords(ruleText);
|
|
46
|
+
if (keywords.length === 0) return false;
|
|
47
|
+
return keywords.every((kw) => eventText.includes(kw));
|
|
48
|
+
}
|
|
49
|
+
function matchesKeywordThreshold(eventText, ruleText, threshold = 0.5) {
|
|
50
|
+
const keywords = extractKeywords(ruleText);
|
|
41
51
|
if (keywords.length === 0) return false;
|
|
42
52
|
const matched = keywords.filter((kw) => eventText.includes(kw));
|
|
43
|
-
return matched.length >= Math.ceil(keywords.length *
|
|
53
|
+
return matched.length >= Math.ceil(keywords.length * threshold);
|
|
44
54
|
}
|
|
45
55
|
function tokenSimilarity(a, b) {
|
|
46
56
|
const tokensA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
@@ -53,6 +63,24 @@ function tokenSimilarity(a, b) {
|
|
|
53
63
|
const union = (/* @__PURE__ */ new Set([...tokensA, ...tokensB])).size;
|
|
54
64
|
return union > 0 ? intersection / union : 0;
|
|
55
65
|
}
|
|
66
|
+
var init_text_utils = __esm({
|
|
67
|
+
"src/engine/text-utils.ts"() {
|
|
68
|
+
"use strict";
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// src/engine/plan-engine.ts
|
|
73
|
+
function keywordMatch(eventText, step) {
|
|
74
|
+
const stepText = [
|
|
75
|
+
step.label,
|
|
76
|
+
step.description ?? "",
|
|
77
|
+
...step.tags ?? []
|
|
78
|
+
].join(" ");
|
|
79
|
+
return matchesKeywordThreshold(eventText, stepText, 0.5);
|
|
80
|
+
}
|
|
81
|
+
function tokenSimilarity2(a, b) {
|
|
82
|
+
return tokenSimilarity(a, b);
|
|
83
|
+
}
|
|
56
84
|
function findMatchingStep(eventText, event, steps) {
|
|
57
85
|
const pendingOrActive = steps.filter((s) => s.status === "pending" || s.status === "active");
|
|
58
86
|
if (pendingOrActive.length === 0) {
|
|
@@ -71,7 +99,7 @@ function findMatchingStep(eventText, event, steps) {
|
|
|
71
99
|
let bestScore = 0;
|
|
72
100
|
for (const step of pendingOrActive) {
|
|
73
101
|
const stepText = [step.label, step.description ?? "", ...step.tags ?? []].join(" ");
|
|
74
|
-
const score =
|
|
102
|
+
const score = tokenSimilarity2(intentText, stepText);
|
|
75
103
|
if (score > bestScore) {
|
|
76
104
|
bestScore = score;
|
|
77
105
|
bestStep = step;
|
|
@@ -112,7 +140,7 @@ function checkConstraints(event, eventText, constraints) {
|
|
|
112
140
|
continue;
|
|
113
141
|
}
|
|
114
142
|
if (constraint.type === "scope" && constraint.trigger) {
|
|
115
|
-
const keywords = constraint.trigger
|
|
143
|
+
const keywords = extractKeywords(constraint.trigger);
|
|
116
144
|
const violated = keywords.length > 0 && keywords.every((kw) => eventText.includes(kw));
|
|
117
145
|
checks.push({
|
|
118
146
|
constraintId: constraint.id,
|
|
@@ -193,11 +221,7 @@ function evaluatePlan(event, plan) {
|
|
|
193
221
|
progress
|
|
194
222
|
};
|
|
195
223
|
}
|
|
196
|
-
const eventText =
|
|
197
|
-
event.intent,
|
|
198
|
-
event.tool ?? "",
|
|
199
|
-
event.scope ?? ""
|
|
200
|
-
].join(" ").toLowerCase();
|
|
224
|
+
const eventText = normalizeEventText(event);
|
|
201
225
|
const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
|
|
202
226
|
if (!matched) {
|
|
203
227
|
return {
|
|
@@ -238,7 +262,7 @@ function evaluatePlan(event, plan) {
|
|
|
238
262
|
};
|
|
239
263
|
}
|
|
240
264
|
function buildPlanCheck(event, plan, verdict) {
|
|
241
|
-
const eventText =
|
|
265
|
+
const eventText = normalizeEventText(event);
|
|
242
266
|
const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
|
|
243
267
|
const { checks: constraintChecks } = checkConstraints(event, eventText, plan.constraints);
|
|
244
268
|
const progress = getPlanProgress(plan);
|
|
@@ -258,12 +282,672 @@ function buildPlanCheck(event, plan, verdict) {
|
|
|
258
282
|
var init_plan_engine = __esm({
|
|
259
283
|
"src/engine/plan-engine.ts"() {
|
|
260
284
|
"use strict";
|
|
285
|
+
init_text_utils();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// src/engine/bootstrap-parser.ts
|
|
290
|
+
var bootstrap_parser_exports = {};
|
|
291
|
+
__export(bootstrap_parser_exports, {
|
|
292
|
+
parseWorldMarkdown: () => parseWorldMarkdown
|
|
293
|
+
});
|
|
294
|
+
function splitSections(markdown) {
|
|
295
|
+
const lines = markdown.split("\n");
|
|
296
|
+
let frontmatter = "";
|
|
297
|
+
let bodyStart = 0;
|
|
298
|
+
if (lines[0]?.trim() === "---") {
|
|
299
|
+
const endIdx = lines.indexOf("---", 1);
|
|
300
|
+
if (endIdx > 0) {
|
|
301
|
+
frontmatter = lines.slice(1, endIdx).join("\n");
|
|
302
|
+
bodyStart = endIdx + 1;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const sections = [];
|
|
306
|
+
let currentSection = null;
|
|
307
|
+
const contentLines = [];
|
|
308
|
+
for (let i = bodyStart; i < lines.length; i++) {
|
|
309
|
+
const line = lines[i];
|
|
310
|
+
if (line.startsWith("# ")) {
|
|
311
|
+
if (currentSection) {
|
|
312
|
+
currentSection.content = contentLines.join("\n").trim();
|
|
313
|
+
sections.push(currentSection);
|
|
314
|
+
contentLines.length = 0;
|
|
315
|
+
}
|
|
316
|
+
currentSection = {
|
|
317
|
+
name: line.replace(/^#\s+/, "").trim(),
|
|
318
|
+
content: "",
|
|
319
|
+
startLine: i + 1
|
|
320
|
+
// 1-based
|
|
321
|
+
};
|
|
322
|
+
} else if (currentSection) {
|
|
323
|
+
contentLines.push(line);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (currentSection) {
|
|
327
|
+
currentSection.content = contentLines.join("\n").trim();
|
|
328
|
+
sections.push(currentSection);
|
|
329
|
+
}
|
|
330
|
+
return { frontmatter, sections };
|
|
331
|
+
}
|
|
332
|
+
function parseFrontmatter2(yaml, issues) {
|
|
333
|
+
const result = {};
|
|
334
|
+
for (const line of yaml.split("\n")) {
|
|
335
|
+
const trimmed = line.trim();
|
|
336
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
337
|
+
const colonIdx = trimmed.indexOf(":");
|
|
338
|
+
if (colonIdx === -1) continue;
|
|
339
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
340
|
+
const value = trimmed.slice(colonIdx + 1).trim();
|
|
341
|
+
result[key] = value;
|
|
342
|
+
}
|
|
343
|
+
if (!result.world_id) {
|
|
344
|
+
issues.push({ line: 1, section: "frontmatter", message: "Missing world_id in frontmatter", severity: "error" });
|
|
345
|
+
}
|
|
346
|
+
if (!result.name) {
|
|
347
|
+
issues.push({ line: 1, section: "frontmatter", message: "Missing name in frontmatter", severity: "error" });
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
world_id: result.world_id ?? "",
|
|
351
|
+
name: result.name ?? "",
|
|
352
|
+
version: result.version,
|
|
353
|
+
runtime_mode: result.runtime_mode,
|
|
354
|
+
default_profile: result.default_profile,
|
|
355
|
+
alternative_profile: result.alternative_profile
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function parseThesis(content, startLine, issues) {
|
|
359
|
+
const thesis = content.trim();
|
|
360
|
+
if (!thesis) {
|
|
361
|
+
issues.push({ line: startLine, section: "Thesis", message: "Thesis section is empty", severity: "error" });
|
|
362
|
+
}
|
|
363
|
+
return thesis;
|
|
364
|
+
}
|
|
365
|
+
function parseInvariants(content, startLine, issues) {
|
|
366
|
+
const invariants = [];
|
|
367
|
+
const lines = content.split("\n");
|
|
368
|
+
for (let i = 0; i < lines.length; i++) {
|
|
369
|
+
const line = lines[i].trim();
|
|
370
|
+
if (!line.startsWith("- ")) continue;
|
|
371
|
+
const lineNum = startLine + i + 1;
|
|
372
|
+
const match = line.match(
|
|
373
|
+
/^-\s+`([^`]+)`\s*[—–-]\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
374
|
+
);
|
|
375
|
+
if (match) {
|
|
376
|
+
const id = match[1];
|
|
377
|
+
const label = match[2].trim();
|
|
378
|
+
const parens = match[3] ?? "structural, immutable";
|
|
379
|
+
const enforcement = parens.includes("prompt") ? "prompt" : parens.includes("operational") ? "operational" : "structural";
|
|
380
|
+
const mutable = parens.includes("mutable") && !parens.includes("immutable");
|
|
381
|
+
invariants.push({ id, label, enforcement, mutable, line: lineNum });
|
|
382
|
+
} else {
|
|
383
|
+
const fallback = line.match(/^-\s+\*\*([^*]+)\*\*\s*[—–-]\s*(.+)/);
|
|
384
|
+
if (fallback) {
|
|
385
|
+
const id = fallback[1].toLowerCase().replace(/\s+/g, "_");
|
|
386
|
+
const label = fallback[2].trim();
|
|
387
|
+
invariants.push({ id, label, enforcement: "structural", mutable: false, line: lineNum });
|
|
388
|
+
} else {
|
|
389
|
+
issues.push({ line: lineNum, section: "Invariants", message: `Could not parse invariant: "${line}"`, severity: "warning" });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (invariants.length === 0) {
|
|
394
|
+
issues.push({ line: startLine, section: "Invariants", message: "No invariants found", severity: "error" });
|
|
395
|
+
}
|
|
396
|
+
return invariants;
|
|
397
|
+
}
|
|
398
|
+
function parseStateVariables(content, startLine, issues) {
|
|
399
|
+
const variables = [];
|
|
400
|
+
const subSections = splitH2Sections(content, startLine);
|
|
401
|
+
for (const sub of subSections) {
|
|
402
|
+
const props = parseKeyValueBullets(sub.content);
|
|
403
|
+
const lineNum = sub.startLine;
|
|
404
|
+
const type = props.type;
|
|
405
|
+
if (!type) {
|
|
406
|
+
issues.push({ line: lineNum, section: "State", message: `Variable "${sub.name}" missing type`, severity: "error" });
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
let defaultVal = props.default ?? "";
|
|
410
|
+
if (type === "number") {
|
|
411
|
+
defaultVal = parseFloat(String(defaultVal)) || 0;
|
|
412
|
+
} else if (type === "boolean") {
|
|
413
|
+
defaultVal = String(defaultVal).toLowerCase() === "true";
|
|
414
|
+
}
|
|
415
|
+
const variable = {
|
|
416
|
+
id: sub.name,
|
|
417
|
+
type,
|
|
418
|
+
default: defaultVal,
|
|
419
|
+
label: props.label ?? sub.name,
|
|
420
|
+
description: props.description ?? "",
|
|
421
|
+
line: lineNum
|
|
422
|
+
};
|
|
423
|
+
if (type === "number") {
|
|
424
|
+
if (props.min !== void 0) variable.min = parseFloat(props.min);
|
|
425
|
+
if (props.max !== void 0) variable.max = parseFloat(props.max);
|
|
426
|
+
if (props.step !== void 0) variable.step = parseFloat(props.step);
|
|
427
|
+
}
|
|
428
|
+
if (type === "enum" && props.options) {
|
|
429
|
+
variable.options = props.options.split(",").map((s) => s.trim());
|
|
430
|
+
}
|
|
431
|
+
variables.push(variable);
|
|
432
|
+
}
|
|
433
|
+
return variables;
|
|
434
|
+
}
|
|
435
|
+
function parseAssumptions(content, startLine, issues) {
|
|
436
|
+
const profiles = [];
|
|
437
|
+
const subSections = splitH2Sections(content, startLine);
|
|
438
|
+
for (const sub of subSections) {
|
|
439
|
+
const props = parseKeyValueBullets(sub.content);
|
|
440
|
+
const name = props.name ?? sub.name;
|
|
441
|
+
const description = props.description ?? "";
|
|
442
|
+
const parameters = {};
|
|
443
|
+
for (const [key, val] of Object.entries(props)) {
|
|
444
|
+
if (key === "name" || key === "description") continue;
|
|
445
|
+
if (val === "true") parameters[key] = true;
|
|
446
|
+
else if (val === "false") parameters[key] = false;
|
|
447
|
+
else if (!isNaN(Number(val)) && val.trim() !== "") parameters[key] = Number(val);
|
|
448
|
+
else parameters[key] = val;
|
|
449
|
+
}
|
|
450
|
+
profiles.push({
|
|
451
|
+
id: sub.name,
|
|
452
|
+
name,
|
|
453
|
+
description,
|
|
454
|
+
parameters,
|
|
455
|
+
line: sub.startLine
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
return profiles;
|
|
459
|
+
}
|
|
460
|
+
function parseRules(content, startLine, issues) {
|
|
461
|
+
const rules = [];
|
|
462
|
+
const subSections = splitH2Sections(content, startLine);
|
|
463
|
+
for (let ruleIdx = 0; ruleIdx < subSections.length; ruleIdx++) {
|
|
464
|
+
const sub = subSections[ruleIdx];
|
|
465
|
+
const lineNum = sub.startLine;
|
|
466
|
+
const headingMatch = sub.name.match(/^([^:]+):\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/);
|
|
467
|
+
const id = headingMatch ? headingMatch[1].trim() : `rule-${String(ruleIdx + 1).padStart(3, "0")}`;
|
|
468
|
+
const label = headingMatch ? headingMatch[2].trim() : sub.name;
|
|
469
|
+
const severity = headingMatch?.[3]?.trim() ?? "degradation";
|
|
470
|
+
const lines = sub.content.split("\n");
|
|
471
|
+
let description = "";
|
|
472
|
+
const triggers = [];
|
|
473
|
+
const effects = [];
|
|
474
|
+
let collapseCheck;
|
|
475
|
+
const causalParts = {};
|
|
476
|
+
for (let i = 0; i < lines.length; i++) {
|
|
477
|
+
const line = lines[i].trim();
|
|
478
|
+
if (line.startsWith("When ")) {
|
|
479
|
+
const triggerStr = line.slice(5);
|
|
480
|
+
const parts = triggerStr.split(/\s+AND\s+/i);
|
|
481
|
+
for (const part of parts) {
|
|
482
|
+
const trigger = parseTriggerExpression(part.trim());
|
|
483
|
+
if (trigger) triggers.push(trigger);
|
|
484
|
+
else issues.push({ line: lineNum + i, section: "Rules", message: `Could not parse trigger: "${part}"`, severity: "warning" });
|
|
485
|
+
}
|
|
486
|
+
} else if (line.startsWith("Then ")) {
|
|
487
|
+
const effectStr = line.slice(5);
|
|
488
|
+
const parts = effectStr.split(",");
|
|
489
|
+
for (const part of parts) {
|
|
490
|
+
const effect = parseEffectExpression(part.trim());
|
|
491
|
+
if (effect) effects.push(effect);
|
|
492
|
+
else issues.push({ line: lineNum + i, section: "Rules", message: `Could not parse effect: "${part}"`, severity: "warning" });
|
|
493
|
+
}
|
|
494
|
+
} else if (line.startsWith("Collapse:")) {
|
|
495
|
+
const collapseStr = line.slice(9).trim();
|
|
496
|
+
const collapse = parseCollapseExpression(collapseStr);
|
|
497
|
+
if (collapse) collapseCheck = collapse;
|
|
498
|
+
else issues.push({ line: lineNum + i, section: "Rules", message: `Could not parse collapse: "${collapseStr}"`, severity: "warning" });
|
|
499
|
+
} else if (line.startsWith("> ")) {
|
|
500
|
+
const causalMatch = line.match(/^>\s*(trigger|rule|shift|effect):\s*(.+)/i);
|
|
501
|
+
if (causalMatch) {
|
|
502
|
+
causalParts[causalMatch[1].toLowerCase()] = causalMatch[2].trim();
|
|
503
|
+
}
|
|
504
|
+
} else if (line && !line.startsWith("-") && !line.startsWith("#")) {
|
|
505
|
+
if (!description) description = line;
|
|
506
|
+
else description += " " + line;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (triggers.length === 0) {
|
|
510
|
+
issues.push({ line: lineNum, section: "Rules", message: `Rule "${id}" has no triggers (missing "When" line)`, severity: "warning" });
|
|
511
|
+
}
|
|
512
|
+
if (effects.length === 0) {
|
|
513
|
+
issues.push({ line: lineNum, section: "Rules", message: `Rule "${id}" has no effects (missing "Then" line)`, severity: "warning" });
|
|
514
|
+
}
|
|
515
|
+
const causal_translation = Object.keys(causalParts).length > 0 ? {
|
|
516
|
+
trigger_text: causalParts.trigger ?? "",
|
|
517
|
+
rule_text: causalParts.rule ?? "",
|
|
518
|
+
shift_text: causalParts.shift ?? "",
|
|
519
|
+
effect_text: causalParts.effect ?? ""
|
|
520
|
+
} : void 0;
|
|
521
|
+
rules.push({
|
|
522
|
+
id,
|
|
523
|
+
label,
|
|
524
|
+
severity,
|
|
525
|
+
description: description || void 0,
|
|
526
|
+
order: ruleIdx + 1,
|
|
527
|
+
triggers,
|
|
528
|
+
effects,
|
|
529
|
+
collapse_check: collapseCheck,
|
|
530
|
+
causal_translation,
|
|
531
|
+
line: lineNum
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
return rules;
|
|
535
|
+
}
|
|
536
|
+
function parseTriggerExpression(expr) {
|
|
537
|
+
const match = expr.match(
|
|
538
|
+
/^(\w+)\s*(==|!=|>=|<=|>|<|in)\s*(.+?)\s*\[(state|assumption)\]\s*$/
|
|
539
|
+
);
|
|
540
|
+
if (!match) return null;
|
|
541
|
+
const field = match[1];
|
|
542
|
+
const operator = match[2];
|
|
543
|
+
let value = match[3].trim();
|
|
544
|
+
const source = match[4];
|
|
545
|
+
value = parseValueLiteral(value);
|
|
546
|
+
return { field, operator, value, source };
|
|
547
|
+
}
|
|
548
|
+
function parseEffectExpression(expr) {
|
|
549
|
+
const compound = expr.match(/^(\w+)\s*(\*=|\+=|-=)\s*(.+)$/);
|
|
550
|
+
if (compound) {
|
|
551
|
+
const target = compound[1];
|
|
552
|
+
const op = compound[2];
|
|
553
|
+
const value = parseValueLiteral(compound[3].trim());
|
|
554
|
+
const operationMap = {
|
|
555
|
+
"*=": "multiply",
|
|
556
|
+
"+=": "add",
|
|
557
|
+
"-=": "subtract"
|
|
558
|
+
};
|
|
559
|
+
return { target, operation: operationMap[op], value };
|
|
560
|
+
}
|
|
561
|
+
const assignment = expr.match(/^(\w+)\s*=\s*(.+)$/);
|
|
562
|
+
if (assignment) {
|
|
563
|
+
const target = assignment[1];
|
|
564
|
+
const value = parseValueLiteral(assignment[2].trim());
|
|
565
|
+
const operation = typeof value === "boolean" ? "set_boolean" : "set";
|
|
566
|
+
return { target, operation, value };
|
|
567
|
+
}
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
function parseCollapseExpression(expr) {
|
|
571
|
+
const cleaned = expr.replace(/\s*→.*$/, "").trim();
|
|
572
|
+
const match = cleaned.match(/^(\w+)\s*(==|!=|>=|<=|>|<)\s*([\d.]+)$/);
|
|
573
|
+
if (!match) return null;
|
|
574
|
+
return {
|
|
575
|
+
field: match[1],
|
|
576
|
+
operator: match[2],
|
|
577
|
+
value: parseFloat(match[3])
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function parseGates(content, startLine, issues) {
|
|
581
|
+
const gates = [];
|
|
582
|
+
const lines = content.split("\n");
|
|
583
|
+
for (let i = 0; i < lines.length; i++) {
|
|
584
|
+
const line = lines[i].trim();
|
|
585
|
+
if (!line.startsWith("- ")) continue;
|
|
586
|
+
const lineNum = startLine + i + 1;
|
|
587
|
+
const match = line.match(/^-\s+(\w+):\s*(\w+)\s*(==|!=|>=|<=|>|<)\s*([\d.]+)/);
|
|
588
|
+
if (match) {
|
|
589
|
+
gates.push({
|
|
590
|
+
status: match[1],
|
|
591
|
+
field: match[2],
|
|
592
|
+
operator: match[3],
|
|
593
|
+
value: parseFloat(match[4]),
|
|
594
|
+
line: lineNum
|
|
595
|
+
});
|
|
596
|
+
} else {
|
|
597
|
+
issues.push({ line: lineNum, section: "Gates", message: `Could not parse gate: "${line}"`, severity: "warning" });
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (gates.length === 0) {
|
|
601
|
+
issues.push({ line: startLine, section: "Gates", message: "No gates found", severity: "error" });
|
|
602
|
+
}
|
|
603
|
+
return gates;
|
|
604
|
+
}
|
|
605
|
+
function parseOutcomes(content, startLine, issues) {
|
|
606
|
+
const outcomes = [];
|
|
607
|
+
const subSections = splitH2Sections(content, startLine);
|
|
608
|
+
for (const sub of subSections) {
|
|
609
|
+
const props = parseKeyValueBullets(sub.content);
|
|
610
|
+
const outcome = {
|
|
611
|
+
id: sub.name,
|
|
612
|
+
type: props.type ?? "number",
|
|
613
|
+
label: props.label ?? sub.name,
|
|
614
|
+
line: sub.startLine
|
|
615
|
+
};
|
|
616
|
+
if (props.range) {
|
|
617
|
+
const rangeParts = props.range.split("-").map(Number);
|
|
618
|
+
if (rangeParts.length === 2 && !isNaN(rangeParts[0]) && !isNaN(rangeParts[1])) {
|
|
619
|
+
outcome.range = [rangeParts[0], rangeParts[1]];
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
if (props.display) outcome.display = props.display;
|
|
623
|
+
if (props.primary) outcome.primary = props.primary === "true";
|
|
624
|
+
if (props.assignment) outcome.assignment = props.assignment;
|
|
625
|
+
outcomes.push(outcome);
|
|
626
|
+
}
|
|
627
|
+
return outcomes;
|
|
628
|
+
}
|
|
629
|
+
function splitH2Sections(content, baseStartLine) {
|
|
630
|
+
const lines = content.split("\n");
|
|
631
|
+
const sections = [];
|
|
632
|
+
let current = null;
|
|
633
|
+
const contentLines = [];
|
|
634
|
+
for (let i = 0; i < lines.length; i++) {
|
|
635
|
+
const line = lines[i];
|
|
636
|
+
if (line.startsWith("## ")) {
|
|
637
|
+
if (current) {
|
|
638
|
+
current.content = contentLines.join("\n").trim();
|
|
639
|
+
sections.push(current);
|
|
640
|
+
contentLines.length = 0;
|
|
641
|
+
}
|
|
642
|
+
current = {
|
|
643
|
+
name: line.replace(/^##\s+/, "").trim(),
|
|
644
|
+
content: "",
|
|
645
|
+
startLine: baseStartLine + i + 1
|
|
646
|
+
};
|
|
647
|
+
} else if (current) {
|
|
648
|
+
contentLines.push(line);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (current) {
|
|
652
|
+
current.content = contentLines.join("\n").trim();
|
|
653
|
+
sections.push(current);
|
|
654
|
+
}
|
|
655
|
+
return sections;
|
|
656
|
+
}
|
|
657
|
+
function parseKeyValueBullets(content) {
|
|
658
|
+
const result = {};
|
|
659
|
+
for (const line of content.split("\n")) {
|
|
660
|
+
const trimmed = line.trim();
|
|
661
|
+
const match = trimmed.match(/^-\s+(\w[\w\s]*?):\s*(.+)$/);
|
|
662
|
+
if (match) {
|
|
663
|
+
result[match[1].trim().toLowerCase().replace(/\s+/g, "_")] = match[2].trim();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
668
|
+
function parseValueLiteral(raw) {
|
|
669
|
+
if (raw === "true") return true;
|
|
670
|
+
if (raw === "false") return false;
|
|
671
|
+
if (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("'") && raw.endsWith("'")) {
|
|
672
|
+
return raw.slice(1, -1);
|
|
673
|
+
}
|
|
674
|
+
const num = Number(raw);
|
|
675
|
+
if (!isNaN(num) && raw.trim() !== "") return num;
|
|
676
|
+
return raw;
|
|
677
|
+
}
|
|
678
|
+
function parseWorldMarkdown(markdown) {
|
|
679
|
+
const issues = [];
|
|
680
|
+
const { frontmatter: fmRaw, sections } = splitSections(markdown);
|
|
681
|
+
const frontmatter = parseFrontmatter2(fmRaw, issues);
|
|
682
|
+
const findSection = (name) => sections.find((s) => s.name.toLowerCase() === name.toLowerCase());
|
|
683
|
+
const thesisSection = findSection("Thesis");
|
|
684
|
+
const thesis = thesisSection ? parseThesis(thesisSection.content, thesisSection.startLine, issues) : "";
|
|
685
|
+
if (!thesisSection) {
|
|
686
|
+
issues.push({ line: 0, section: "Thesis", message: "Missing # Thesis section", severity: "error" });
|
|
687
|
+
}
|
|
688
|
+
const invariantsSection = findSection("Invariants");
|
|
689
|
+
const invariants = invariantsSection ? parseInvariants(invariantsSection.content, invariantsSection.startLine, issues) : [];
|
|
690
|
+
if (!invariantsSection) {
|
|
691
|
+
issues.push({ line: 0, section: "Invariants", message: "Missing # Invariants section", severity: "error" });
|
|
692
|
+
}
|
|
693
|
+
const stateSection = findSection("State");
|
|
694
|
+
const stateVariables = stateSection ? parseStateVariables(stateSection.content, stateSection.startLine, issues) : [];
|
|
695
|
+
if (!stateSection) {
|
|
696
|
+
issues.push({ line: 0, section: "State", message: "Missing # State section", severity: "warning" });
|
|
697
|
+
}
|
|
698
|
+
const assumptionsSection = findSection("Assumptions");
|
|
699
|
+
const assumptions = assumptionsSection ? parseAssumptions(assumptionsSection.content, assumptionsSection.startLine, issues) : [];
|
|
700
|
+
if (!assumptionsSection) {
|
|
701
|
+
issues.push({ line: 0, section: "Assumptions", message: "Missing # Assumptions section", severity: "warning" });
|
|
702
|
+
}
|
|
703
|
+
const rulesSection = findSection("Rules");
|
|
704
|
+
const rules = rulesSection ? parseRules(rulesSection.content, rulesSection.startLine, issues) : [];
|
|
705
|
+
if (!rulesSection) {
|
|
706
|
+
issues.push({ line: 0, section: "Rules", message: "Missing # Rules section", severity: "warning" });
|
|
707
|
+
}
|
|
708
|
+
const gatesSection = findSection("Gates");
|
|
709
|
+
const gates = gatesSection ? parseGates(gatesSection.content, gatesSection.startLine, issues) : [];
|
|
710
|
+
if (!gatesSection) {
|
|
711
|
+
issues.push({ line: 0, section: "Gates", message: "Missing # Gates section", severity: "warning" });
|
|
712
|
+
}
|
|
713
|
+
const outcomesSection = findSection("Outcomes");
|
|
714
|
+
const outcomes = outcomesSection ? parseOutcomes(outcomesSection.content, outcomesSection.startLine, issues) : [];
|
|
715
|
+
const parsedSections = sections.map((s) => s.name);
|
|
716
|
+
const knownSections = /* @__PURE__ */ new Set(["thesis", "invariants", "state", "assumptions", "rules", "gates", "outcomes"]);
|
|
717
|
+
for (const section of sections) {
|
|
718
|
+
if (!knownSections.has(section.name.toLowerCase())) {
|
|
719
|
+
issues.push({
|
|
720
|
+
line: section.startLine,
|
|
721
|
+
section: section.name,
|
|
722
|
+
message: `Unrecognized section "${section.name}" \u2014 will be ignored`,
|
|
723
|
+
severity: "info"
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const hasErrors = issues.some((i) => i.severity === "error");
|
|
728
|
+
if (!frontmatter.world_id || !thesis) {
|
|
729
|
+
if (hasErrors) {
|
|
730
|
+
return { world: null, issues };
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return {
|
|
734
|
+
world: {
|
|
735
|
+
frontmatter,
|
|
736
|
+
thesis,
|
|
737
|
+
invariants,
|
|
738
|
+
stateVariables,
|
|
739
|
+
assumptions,
|
|
740
|
+
rules,
|
|
741
|
+
gates,
|
|
742
|
+
outcomes
|
|
743
|
+
},
|
|
744
|
+
issues
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
var init_bootstrap_parser = __esm({
|
|
748
|
+
"src/engine/bootstrap-parser.ts"() {
|
|
749
|
+
"use strict";
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
// src/engine/bootstrap-emitter.ts
|
|
754
|
+
var bootstrap_emitter_exports = {};
|
|
755
|
+
__export(bootstrap_emitter_exports, {
|
|
756
|
+
emitWorldDefinition: () => emitWorldDefinition
|
|
757
|
+
});
|
|
758
|
+
function emitWorldDefinition(parsed) {
|
|
759
|
+
const issues = [];
|
|
760
|
+
const fm = parsed.frontmatter;
|
|
761
|
+
const defaultProfile = fm.default_profile ?? parsed.assumptions[0]?.id ?? "baseline";
|
|
762
|
+
const altProfile = fm.alternative_profile ?? parsed.assumptions[1]?.id ?? "alternative";
|
|
763
|
+
const world = {
|
|
764
|
+
world_id: fm.world_id,
|
|
765
|
+
name: fm.name,
|
|
766
|
+
thesis: parsed.thesis,
|
|
767
|
+
version: fm.version ?? "1.0.0",
|
|
768
|
+
runtime_mode: fm.runtime_mode ?? "SIMULATION",
|
|
769
|
+
default_assumption_profile: defaultProfile,
|
|
770
|
+
default_alternative_profile: altProfile,
|
|
771
|
+
modules: parsed.rules.map((r) => r.id),
|
|
772
|
+
players: {
|
|
773
|
+
thinking_space: true,
|
|
774
|
+
experience_space: true,
|
|
775
|
+
action_space: true
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
const invariants = parsed.invariants.map((inv) => ({
|
|
779
|
+
id: inv.id,
|
|
780
|
+
label: inv.label,
|
|
781
|
+
enforcement: inv.enforcement === "prompt" ? "prompt" : "structural",
|
|
782
|
+
mutable: false
|
|
783
|
+
}));
|
|
784
|
+
const profiles = {};
|
|
785
|
+
const parameterDefinitions = {};
|
|
786
|
+
for (let i = 0; i < parsed.assumptions.length; i++) {
|
|
787
|
+
const profile = parsed.assumptions[i];
|
|
788
|
+
const params = {};
|
|
789
|
+
for (const [key, val] of Object.entries(profile.parameters)) {
|
|
790
|
+
params[key] = String(val);
|
|
791
|
+
if (!parameterDefinitions[key]) {
|
|
792
|
+
parameterDefinitions[key] = {
|
|
793
|
+
type: typeof val === "boolean" ? "boolean" : typeof val === "number" ? "number" : "enum",
|
|
794
|
+
label: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
795
|
+
description: `Parameter: ${key}`
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
profiles[profile.id] = {
|
|
800
|
+
name: profile.name,
|
|
801
|
+
description: profile.description,
|
|
802
|
+
is_default_baseline: i === 0 || profile.id === defaultProfile,
|
|
803
|
+
is_default_alternative: i === 1 || profile.id === altProfile,
|
|
804
|
+
parameters: params
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
const assumptions = { profiles, parameter_definitions: parameterDefinitions };
|
|
808
|
+
const variables = {};
|
|
809
|
+
for (const v of parsed.stateVariables) {
|
|
810
|
+
const stateVar = {
|
|
811
|
+
type: v.type,
|
|
812
|
+
default: v.default,
|
|
813
|
+
mutable: true,
|
|
814
|
+
label: v.label,
|
|
815
|
+
description: v.description
|
|
816
|
+
};
|
|
817
|
+
if (v.type === "number") {
|
|
818
|
+
if (v.min !== void 0) stateVar.min = v.min;
|
|
819
|
+
if (v.max !== void 0) stateVar.max = v.max;
|
|
820
|
+
if (v.step !== void 0) stateVar.step = v.step;
|
|
821
|
+
}
|
|
822
|
+
if (v.type === "enum" && v.options) {
|
|
823
|
+
stateVar.options = v.options;
|
|
824
|
+
}
|
|
825
|
+
variables[v.id] = stateVar;
|
|
826
|
+
}
|
|
827
|
+
const stateSchema = { variables, presets: {} };
|
|
828
|
+
const rules = parsed.rules.map((r) => {
|
|
829
|
+
const triggers = r.triggers.map((t) => ({
|
|
830
|
+
field: t.field,
|
|
831
|
+
operator: t.operator,
|
|
832
|
+
value: t.value,
|
|
833
|
+
source: t.source
|
|
834
|
+
}));
|
|
835
|
+
const effects = r.effects.map((e) => ({
|
|
836
|
+
target: e.target,
|
|
837
|
+
operation: e.operation,
|
|
838
|
+
value: e.value
|
|
839
|
+
}));
|
|
840
|
+
let collapse_check;
|
|
841
|
+
if (r.collapse_check) {
|
|
842
|
+
collapse_check = {
|
|
843
|
+
field: r.collapse_check.field,
|
|
844
|
+
operator: r.collapse_check.operator,
|
|
845
|
+
value: r.collapse_check.value,
|
|
846
|
+
result: "MODEL_COLLAPSES"
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
const causal_translation = r.causal_translation ?? {
|
|
850
|
+
trigger_text: "",
|
|
851
|
+
rule_text: "",
|
|
852
|
+
shift_text: "",
|
|
853
|
+
effect_text: ""
|
|
854
|
+
};
|
|
855
|
+
const rule = {
|
|
856
|
+
id: r.id,
|
|
857
|
+
severity: r.severity,
|
|
858
|
+
label: r.label,
|
|
859
|
+
description: r.description ?? r.label,
|
|
860
|
+
order: r.order,
|
|
861
|
+
triggers,
|
|
862
|
+
effects: effects.length > 0 ? effects : void 0,
|
|
863
|
+
collapse_check,
|
|
864
|
+
causal_translation
|
|
865
|
+
};
|
|
866
|
+
return rule;
|
|
867
|
+
});
|
|
868
|
+
const viabilityClassification = parsed.gates.map((g) => {
|
|
869
|
+
const defaults = GATE_DEFAULTS[g.status] ?? { color: "#5c5a52", icon: "\u25CF" };
|
|
870
|
+
return {
|
|
871
|
+
status: g.status,
|
|
872
|
+
field: g.field,
|
|
873
|
+
operator: g.operator,
|
|
874
|
+
value: g.value,
|
|
875
|
+
color: defaults.color,
|
|
876
|
+
icon: defaults.icon
|
|
877
|
+
};
|
|
878
|
+
});
|
|
879
|
+
const gates = {
|
|
880
|
+
viability_classification: viabilityClassification,
|
|
881
|
+
structural_override: {
|
|
882
|
+
description: "Rules with severity=structural and triggered collapse_check force MODEL_COLLAPSES regardless of final margin.",
|
|
883
|
+
enforcement: "mandatory"
|
|
884
|
+
},
|
|
885
|
+
sustainability_threshold: 0.1,
|
|
886
|
+
collapse_visual: {
|
|
887
|
+
background: "#1c1917",
|
|
888
|
+
text: "#fef2f2",
|
|
889
|
+
border: "#b91c1c",
|
|
890
|
+
label: "Structural Failure"
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
const computedOutcomes = parsed.outcomes.map((o) => {
|
|
894
|
+
const outcome = {
|
|
895
|
+
id: o.id,
|
|
896
|
+
type: o.type,
|
|
897
|
+
label: o.label,
|
|
898
|
+
show_in_comparison: true
|
|
899
|
+
};
|
|
900
|
+
if (o.range) outcome.range = o.range;
|
|
901
|
+
if (o.display) outcome.display_as = o.display;
|
|
902
|
+
if (o.primary) outcome.primary = o.primary;
|
|
903
|
+
if (o.assignment) outcome.assignment = o.assignment;
|
|
904
|
+
return outcome;
|
|
905
|
+
});
|
|
906
|
+
const outcomes = {
|
|
907
|
+
computed_outcomes: computedOutcomes,
|
|
908
|
+
comparison_layout: {
|
|
909
|
+
primary_card: computedOutcomes.find((o) => o.primary)?.id ?? computedOutcomes[0]?.id ?? "",
|
|
910
|
+
status_badge: "viability_status",
|
|
911
|
+
structural_indicators: rules.filter((r) => r.severity === "structural").map((r) => r.id)
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
const metadata = {
|
|
915
|
+
format_version: "1.0.0",
|
|
916
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
917
|
+
last_modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
918
|
+
authoring_method: "manual-authoring"
|
|
919
|
+
};
|
|
920
|
+
const worldDefinition = {
|
|
921
|
+
world,
|
|
922
|
+
invariants,
|
|
923
|
+
assumptions,
|
|
924
|
+
stateSchema,
|
|
925
|
+
rules,
|
|
926
|
+
gates,
|
|
927
|
+
outcomes,
|
|
928
|
+
metadata
|
|
929
|
+
};
|
|
930
|
+
return { world: worldDefinition, issues };
|
|
931
|
+
}
|
|
932
|
+
var GATE_DEFAULTS;
|
|
933
|
+
var init_bootstrap_emitter = __esm({
|
|
934
|
+
"src/engine/bootstrap-emitter.ts"() {
|
|
935
|
+
"use strict";
|
|
936
|
+
GATE_DEFAULTS = {
|
|
937
|
+
THRIVING: { color: "#0f6b3a", icon: "\u2726" },
|
|
938
|
+
STABLE: { color: "#1856b8", icon: "\u25CF" },
|
|
939
|
+
COMPRESSED: { color: "#a16207", icon: "\u25B2" },
|
|
940
|
+
CRITICAL: { color: "#b91c1c", icon: "\u26A0" },
|
|
941
|
+
MODEL_COLLAPSES: { color: "#7f1d1d", icon: "\u2715" }
|
|
942
|
+
};
|
|
261
943
|
}
|
|
262
944
|
});
|
|
263
945
|
|
|
264
946
|
// src/loader/world-loader.ts
|
|
265
947
|
var world_loader_exports = {};
|
|
266
948
|
__export(world_loader_exports, {
|
|
949
|
+
DEFAULT_BUNDLED_WORLD: () => DEFAULT_BUNDLED_WORLD,
|
|
950
|
+
loadBundledWorld: () => loadBundledWorld,
|
|
267
951
|
loadWorld: () => loadWorld,
|
|
268
952
|
loadWorldFromDirectory: () => loadWorldFromDirectory
|
|
269
953
|
});
|
|
@@ -272,10 +956,18 @@ async function loadWorldFromDirectory(dirPath) {
|
|
|
272
956
|
const { join } = await import("path");
|
|
273
957
|
const { readdirSync } = await import("fs");
|
|
274
958
|
async function readJson(filename) {
|
|
959
|
+
const filePath = join(dirPath, filename);
|
|
275
960
|
try {
|
|
276
|
-
const content = await readFile(
|
|
961
|
+
const content = await readFile(filePath, "utf-8");
|
|
277
962
|
return JSON.parse(content);
|
|
278
|
-
} catch {
|
|
963
|
+
} catch (err) {
|
|
964
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
965
|
+
return void 0;
|
|
966
|
+
}
|
|
967
|
+
process.stderr.write(
|
|
968
|
+
`[neuroverse] Warning: Failed to read ${filename}: ${err instanceof Error ? err.message : String(err)}
|
|
969
|
+
`
|
|
970
|
+
);
|
|
279
971
|
return void 0;
|
|
280
972
|
}
|
|
281
973
|
}
|
|
@@ -297,10 +989,23 @@ async function loadWorldFromDirectory(dirPath) {
|
|
|
297
989
|
const rulesDir = join(dirPath, "rules");
|
|
298
990
|
const ruleFiles = readdirSync(rulesDir).filter((f) => f.endsWith(".json")).sort();
|
|
299
991
|
for (const file of ruleFiles) {
|
|
300
|
-
|
|
301
|
-
|
|
992
|
+
try {
|
|
993
|
+
const content = await readFile(join(rulesDir, file), "utf-8");
|
|
994
|
+
rules.push(JSON.parse(content));
|
|
995
|
+
} catch (err) {
|
|
996
|
+
process.stderr.write(
|
|
997
|
+
`[neuroverse] Warning: Failed to parse rule ${file}: ${err instanceof Error ? err.message : String(err)}
|
|
998
|
+
`
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
} catch (err) {
|
|
1003
|
+
if (!(err instanceof Error && "code" in err && err.code === "ENOENT")) {
|
|
1004
|
+
process.stderr.write(
|
|
1005
|
+
`[neuroverse] Warning: Failed to read rules directory: ${err instanceof Error ? err.message : String(err)}
|
|
1006
|
+
`
|
|
1007
|
+
);
|
|
302
1008
|
}
|
|
303
|
-
} catch {
|
|
304
1009
|
}
|
|
305
1010
|
return {
|
|
306
1011
|
world: worldJson,
|
|
@@ -335,14 +1040,49 @@ async function loadWorld(worldPath) {
|
|
|
335
1040
|
if (info.isDirectory()) {
|
|
336
1041
|
return loadWorldFromDirectory(worldPath);
|
|
337
1042
|
}
|
|
338
|
-
|
|
339
|
-
|
|
1043
|
+
throw new Error(`Cannot load world from: ${worldPath} \u2014 expected a directory`);
|
|
1044
|
+
}
|
|
1045
|
+
async function loadBundledWorld(name = DEFAULT_BUNDLED_WORLD) {
|
|
1046
|
+
const { readFile } = await import("fs/promises");
|
|
1047
|
+
const { join, dirname } = await import("path");
|
|
1048
|
+
const { existsSync } = await import("fs");
|
|
1049
|
+
const { fileURLToPath } = await import("url");
|
|
1050
|
+
const { parseWorldMarkdown: parseWorldMarkdown2 } = await Promise.resolve().then(() => (init_bootstrap_parser(), bootstrap_parser_exports));
|
|
1051
|
+
const { emitWorldDefinition: emitWorldDefinition2 } = await Promise.resolve().then(() => (init_bootstrap_emitter(), bootstrap_emitter_exports));
|
|
1052
|
+
const filename = `${name}.nv-world.md`;
|
|
1053
|
+
let packageRoot;
|
|
1054
|
+
try {
|
|
1055
|
+
const thisFile = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import_meta.url));
|
|
1056
|
+
packageRoot = join(thisFile, "..", "..");
|
|
1057
|
+
} catch {
|
|
1058
|
+
packageRoot = process.cwd();
|
|
1059
|
+
}
|
|
1060
|
+
const candidates = [
|
|
1061
|
+
join(packageRoot, "dist", "worlds", filename),
|
|
1062
|
+
join(packageRoot, "src", "worlds", filename)
|
|
1063
|
+
];
|
|
1064
|
+
for (const candidate of candidates) {
|
|
1065
|
+
if (existsSync(candidate)) {
|
|
1066
|
+
const markdown = await readFile(candidate, "utf-8");
|
|
1067
|
+
const parsed = parseWorldMarkdown2(markdown);
|
|
1068
|
+
if (!parsed.world) {
|
|
1069
|
+
throw new Error(`Failed to parse bundled world: ${candidate}`);
|
|
1070
|
+
}
|
|
1071
|
+
const { world } = emitWorldDefinition2(parsed.world);
|
|
1072
|
+
return world;
|
|
1073
|
+
}
|
|
340
1074
|
}
|
|
341
|
-
throw new Error(
|
|
1075
|
+
throw new Error(
|
|
1076
|
+
`Bundled world "${name}" not found. Searched:
|
|
1077
|
+
` + candidates.map((c) => ` ${c}`).join("\n")
|
|
1078
|
+
);
|
|
342
1079
|
}
|
|
1080
|
+
var import_meta, DEFAULT_BUNDLED_WORLD;
|
|
343
1081
|
var init_world_loader = __esm({
|
|
344
1082
|
"src/loader/world-loader.ts"() {
|
|
345
1083
|
"use strict";
|
|
1084
|
+
import_meta = {};
|
|
1085
|
+
DEFAULT_BUNDLED_WORLD = "coding-agent";
|
|
346
1086
|
}
|
|
347
1087
|
});
|
|
348
1088
|
|
|
@@ -376,7 +1116,44 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
376
1116
|
const startTime = performance.now();
|
|
377
1117
|
const level = options.level ?? "standard";
|
|
378
1118
|
const includeTrace = options.trace ?? false;
|
|
379
|
-
|
|
1119
|
+
if (!event.intent || typeof event.intent !== "string") {
|
|
1120
|
+
return {
|
|
1121
|
+
status: "BLOCK",
|
|
1122
|
+
reason: "GuardEvent.intent is required and must be a string",
|
|
1123
|
+
ruleId: "safety-input-validation",
|
|
1124
|
+
evidence: {
|
|
1125
|
+
worldId: world.world?.world_id ?? "",
|
|
1126
|
+
worldName: world.world?.name ?? "",
|
|
1127
|
+
worldVersion: world.world?.version ?? "",
|
|
1128
|
+
evaluatedAt: Date.now(),
|
|
1129
|
+
invariantsSatisfied: 0,
|
|
1130
|
+
invariantsTotal: 0,
|
|
1131
|
+
guardsMatched: [],
|
|
1132
|
+
rulesMatched: [],
|
|
1133
|
+
enforcementLevel: level
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
const inputLength = event.intent.length + (event.tool?.length ?? 0) + (event.scope?.length ?? 0) + (event.payload ? JSON.stringify(event.payload).length : 0);
|
|
1138
|
+
if (inputLength > MAX_INPUT_LENGTH) {
|
|
1139
|
+
return {
|
|
1140
|
+
status: "BLOCK",
|
|
1141
|
+
reason: `Input exceeds maximum allowed length (${MAX_INPUT_LENGTH} characters)`,
|
|
1142
|
+
ruleId: "safety-input-length",
|
|
1143
|
+
evidence: {
|
|
1144
|
+
worldId: world.world?.world_id ?? "",
|
|
1145
|
+
worldName: world.world?.name ?? "",
|
|
1146
|
+
worldVersion: world.world?.version ?? "",
|
|
1147
|
+
evaluatedAt: Date.now(),
|
|
1148
|
+
invariantsSatisfied: 0,
|
|
1149
|
+
invariantsTotal: 0,
|
|
1150
|
+
guardsMatched: [],
|
|
1151
|
+
rulesMatched: [],
|
|
1152
|
+
enforcementLevel: level
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
const eventText = normalizeEventText(event);
|
|
380
1157
|
const invariantChecks = [];
|
|
381
1158
|
const safetyChecks = [];
|
|
382
1159
|
let planCheckResult;
|
|
@@ -389,6 +1166,43 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
389
1166
|
const guardsMatched = [];
|
|
390
1167
|
const rulesMatched = [];
|
|
391
1168
|
checkInvariantCoverage(world, invariantChecks);
|
|
1169
|
+
if (event.roleId && options.agentStates) {
|
|
1170
|
+
const agentState = options.agentStates.get(event.roleId);
|
|
1171
|
+
if (agentState && agentState.cooldownRemaining > 0) {
|
|
1172
|
+
decidingLayer = "safety";
|
|
1173
|
+
decidingId = `penalize-cooldown-${event.roleId}`;
|
|
1174
|
+
const verdict = buildVerdict(
|
|
1175
|
+
"PENALIZE",
|
|
1176
|
+
`Agent "${event.roleId}" is frozen for ${agentState.cooldownRemaining} more round(s) due to prior penalty.`,
|
|
1177
|
+
`penalize-cooldown-${event.roleId}`,
|
|
1178
|
+
void 0,
|
|
1179
|
+
world,
|
|
1180
|
+
level,
|
|
1181
|
+
invariantChecks,
|
|
1182
|
+
guardsMatched,
|
|
1183
|
+
rulesMatched,
|
|
1184
|
+
includeTrace ? buildTrace(
|
|
1185
|
+
invariantChecks,
|
|
1186
|
+
safetyChecks,
|
|
1187
|
+
planCheckResult,
|
|
1188
|
+
roleChecks,
|
|
1189
|
+
guardChecks,
|
|
1190
|
+
kernelRuleChecks,
|
|
1191
|
+
levelChecks,
|
|
1192
|
+
decidingLayer,
|
|
1193
|
+
decidingId,
|
|
1194
|
+
startTime
|
|
1195
|
+
) : void 0
|
|
1196
|
+
);
|
|
1197
|
+
verdict.intentRecord = {
|
|
1198
|
+
originalIntent: event.intent,
|
|
1199
|
+
finalAction: "blocked (agent frozen)",
|
|
1200
|
+
enforcement: "PENALIZE",
|
|
1201
|
+
consequence: { type: "freeze", rounds: agentState.cooldownRemaining, description: "Agent still in cooldown from prior penalty" }
|
|
1202
|
+
};
|
|
1203
|
+
return verdict;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
392
1206
|
if (options.sessionAllowlist) {
|
|
393
1207
|
const key = eventToAllowlistKey(event);
|
|
394
1208
|
if (options.sessionAllowlist.has(key)) {
|
|
@@ -516,7 +1330,16 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
516
1330
|
if (guardVerdict.status !== "ALLOW") {
|
|
517
1331
|
decidingLayer = "guard";
|
|
518
1332
|
decidingId = guardVerdict.ruleId;
|
|
519
|
-
|
|
1333
|
+
const intentRecord = {
|
|
1334
|
+
originalIntent: event.intent,
|
|
1335
|
+
finalAction: guardVerdict.status === "MODIFY" ? guardVerdict.modifiedTo ?? "modified" : guardVerdict.status === "PENALIZE" ? "blocked + penalized" : guardVerdict.status === "REWARD" ? event.intent : guardVerdict.status === "NEUTRAL" ? event.intent : guardVerdict.status === "BLOCK" ? "blocked" : "paused",
|
|
1336
|
+
ruleApplied: guardVerdict.ruleId,
|
|
1337
|
+
enforcement: guardVerdict.status,
|
|
1338
|
+
modifiedTo: guardVerdict.modifiedTo,
|
|
1339
|
+
consequence: guardVerdict.consequence,
|
|
1340
|
+
reward: guardVerdict.reward
|
|
1341
|
+
};
|
|
1342
|
+
const verdict = buildVerdict(
|
|
520
1343
|
guardVerdict.status,
|
|
521
1344
|
guardVerdict.reason,
|
|
522
1345
|
guardVerdict.ruleId,
|
|
@@ -539,6 +1362,10 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
539
1362
|
startTime
|
|
540
1363
|
) : void 0
|
|
541
1364
|
);
|
|
1365
|
+
verdict.intentRecord = intentRecord;
|
|
1366
|
+
if (guardVerdict.consequence) verdict.consequence = guardVerdict.consequence;
|
|
1367
|
+
if (guardVerdict.reward) verdict.reward = guardVerdict.reward;
|
|
1368
|
+
return verdict;
|
|
542
1369
|
}
|
|
543
1370
|
}
|
|
544
1371
|
const kernelVerdict = checkKernelRules(eventText, world, kernelRuleChecks, rulesMatched);
|
|
@@ -833,6 +1660,21 @@ function checkGuards(event, eventText, world, checks, guardsMatched) {
|
|
|
833
1660
|
if (actionMode === "pause") {
|
|
834
1661
|
return { status: "PAUSE", reason, ruleId: `guard-${guard.id}` };
|
|
835
1662
|
}
|
|
1663
|
+
if (actionMode === "penalize") {
|
|
1664
|
+
const consequence = guard.consequence ? { ...guard.consequence } : { type: "freeze", rounds: 1, description: `Penalized for violating: ${guard.label}` };
|
|
1665
|
+
return { status: "PENALIZE", reason, ruleId: `guard-${guard.id}`, consequence };
|
|
1666
|
+
}
|
|
1667
|
+
if (actionMode === "reward") {
|
|
1668
|
+
const reward = guard.reward ? { ...guard.reward } : { type: "boost_influence", magnitude: 0.1, description: `Rewarded for: ${guard.label}` };
|
|
1669
|
+
return { status: "REWARD", reason, ruleId: `guard-${guard.id}`, reward };
|
|
1670
|
+
}
|
|
1671
|
+
if (actionMode === "modify") {
|
|
1672
|
+
const modifiedTo = guard.modify_to ?? guard.redirect ?? "hold";
|
|
1673
|
+
return { status: "MODIFY", reason: `${reason} \u2192 Modified to: ${modifiedTo}`, ruleId: `guard-${guard.id}`, modifiedTo };
|
|
1674
|
+
}
|
|
1675
|
+
if (actionMode === "neutral") {
|
|
1676
|
+
return { status: "NEUTRAL", reason, ruleId: `guard-${guard.id}` };
|
|
1677
|
+
}
|
|
836
1678
|
if (actionMode === "warn" && !warnResult) {
|
|
837
1679
|
warnResult = { status: "ALLOW", warning: reason, ruleId: `guard-${guard.id}` };
|
|
838
1680
|
}
|
|
@@ -942,9 +1784,7 @@ function checkLevelConstraints(event, level, checks) {
|
|
|
942
1784
|
return null;
|
|
943
1785
|
}
|
|
944
1786
|
function matchesKeywords(eventText, ruleText) {
|
|
945
|
-
|
|
946
|
-
if (keywords.length === 0) return false;
|
|
947
|
-
return keywords.every((kw) => eventText.includes(kw));
|
|
1787
|
+
return matchesAllKeywords(eventText, ruleText);
|
|
948
1788
|
}
|
|
949
1789
|
function eventToAllowlistKey(event) {
|
|
950
1790
|
return `${(event.tool ?? "*").toLowerCase()}::${event.intent.toLowerCase().trim()}`;
|
|
@@ -1005,11 +1845,12 @@ function buildVerdict(status, reason, ruleId, warning, world, level, invariantCh
|
|
|
1005
1845
|
if (trace) verdict.trace = trace;
|
|
1006
1846
|
return verdict;
|
|
1007
1847
|
}
|
|
1008
|
-
var PROMPT_INJECTION_PATTERNS, EXECUTION_CLAIM_PATTERNS, EXECUTION_INTENT_PATTERNS, SCOPE_ESCAPE_PATTERNS, NEUTRAL_MESSAGES;
|
|
1848
|
+
var PROMPT_INJECTION_PATTERNS, EXECUTION_CLAIM_PATTERNS, EXECUTION_INTENT_PATTERNS, SCOPE_ESCAPE_PATTERNS, NEUTRAL_MESSAGES, MAX_INPUT_LENGTH;
|
|
1009
1849
|
var init_guard_engine = __esm({
|
|
1010
1850
|
"src/engine/guard-engine.ts"() {
|
|
1011
1851
|
"use strict";
|
|
1012
1852
|
init_plan_engine();
|
|
1853
|
+
init_text_utils();
|
|
1013
1854
|
PROMPT_INJECTION_PATTERNS = [
|
|
1014
1855
|
// Instruction override
|
|
1015
1856
|
{ pattern: /ignore\s+(previous|all|prior|above)\s+(instructions?|rules?)/i, label: "ignore-instructions" },
|
|
@@ -1073,6 +1914,7 @@ var init_guard_engine = __esm({
|
|
|
1073
1914
|
"network-mutate": "This action would send data to an external service.",
|
|
1074
1915
|
"credential-access": "This action would access stored credentials."
|
|
1075
1916
|
};
|
|
1917
|
+
MAX_INPUT_LENGTH = 1e5;
|
|
1076
1918
|
}
|
|
1077
1919
|
});
|
|
1078
1920
|
|
|
@@ -1247,22 +2089,20 @@ var PLAN_EXIT_CODES = {
|
|
|
1247
2089
|
PLAN_COMPLETE: 4
|
|
1248
2090
|
};
|
|
1249
2091
|
|
|
2092
|
+
// src/cli/cli-utils.ts
|
|
2093
|
+
async function readStdin() {
|
|
2094
|
+
const chunks = [];
|
|
2095
|
+
for await (const chunk of process.stdin) {
|
|
2096
|
+
chunks.push(chunk);
|
|
2097
|
+
}
|
|
2098
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
2099
|
+
}
|
|
2100
|
+
|
|
1250
2101
|
// src/cli/plan.ts
|
|
1251
2102
|
function parseArg(args, flag) {
|
|
1252
2103
|
const idx = args.indexOf(flag);
|
|
1253
2104
|
return idx >= 0 && idx + 1 < args.length ? args[idx + 1] : void 0;
|
|
1254
2105
|
}
|
|
1255
|
-
function readStdin() {
|
|
1256
|
-
return new Promise((resolve, reject) => {
|
|
1257
|
-
let data = "";
|
|
1258
|
-
process.stdin.setEncoding("utf-8");
|
|
1259
|
-
process.stdin.on("data", (chunk) => {
|
|
1260
|
-
data += chunk;
|
|
1261
|
-
});
|
|
1262
|
-
process.stdin.on("end", () => resolve(data));
|
|
1263
|
-
process.stdin.on("error", reject);
|
|
1264
|
-
});
|
|
1265
|
-
}
|
|
1266
2106
|
async function compileCommand(args) {
|
|
1267
2107
|
const inputPath = args.find((a) => !a.startsWith("--"));
|
|
1268
2108
|
if (!inputPath) {
|