@neuroverseos/governance 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE.md +190 -0
  2. package/README.md +472 -115
  3. package/dist/adapters/express.cjs +874 -0
  4. package/dist/adapters/express.d.cts +66 -0
  5. package/dist/adapters/express.d.ts +66 -0
  6. package/dist/adapters/express.js +10 -0
  7. package/dist/adapters/index.cjs +1166 -0
  8. package/dist/adapters/index.d.cts +5 -0
  9. package/dist/adapters/index.d.ts +5 -0
  10. package/dist/adapters/index.js +40 -0
  11. package/dist/adapters/langchain.cjs +861 -0
  12. package/dist/adapters/langchain.d.cts +82 -0
  13. package/dist/adapters/langchain.d.ts +82 -0
  14. package/dist/adapters/langchain.js +14 -0
  15. package/dist/adapters/openai.cjs +891 -0
  16. package/dist/adapters/openai.d.cts +92 -0
  17. package/dist/adapters/openai.d.ts +92 -0
  18. package/dist/adapters/openai.js +14 -0
  19. package/dist/adapters/openclaw.cjs +883 -0
  20. package/dist/adapters/openclaw.d.cts +92 -0
  21. package/dist/adapters/openclaw.d.ts +92 -0
  22. package/dist/adapters/openclaw.js +14 -0
  23. package/dist/bootstrap-H4HHKQ5G.js +113 -0
  24. package/dist/build-73KAVHEY.js +308 -0
  25. package/dist/chunk-4NGDRRQH.js +10 -0
  26. package/dist/chunk-B4NF3OLW.js +673 -0
  27. package/dist/chunk-BUWWN2NX.js +192 -0
  28. package/dist/chunk-CROPZ75A.js +107 -0
  29. package/dist/chunk-D7BGWV2J.js +620 -0
  30. package/dist/chunk-EIUHJXBB.js +340 -0
  31. package/dist/chunk-EQXFOKH2.js +85 -0
  32. package/dist/chunk-FYPYZFV5.js +187 -0
  33. package/dist/chunk-FYS2CBUW.js +304 -0
  34. package/dist/chunk-FZQCRGUU.js +115 -0
  35. package/dist/chunk-I3RRAYK2.js +11 -0
  36. package/dist/chunk-ITJ3LCPG.js +225 -0
  37. package/dist/chunk-M3TZFGHO.js +79 -0
  38. package/dist/chunk-MWDQ4MJB.js +11 -0
  39. package/dist/chunk-O5OMJMIE.js +590 -0
  40. package/dist/chunk-T4X42QXC.js +121 -0
  41. package/dist/chunk-XPDMYECO.js +642 -0
  42. package/dist/chunk-Z2S2HIV5.js +100 -0
  43. package/dist/cli/neuroverse.cjs +5864 -0
  44. package/dist/cli/neuroverse.d.cts +1 -0
  45. package/dist/cli/neuroverse.d.ts +1 -0
  46. package/dist/cli/neuroverse.js +128 -0
  47. package/dist/configure-ai-46JVG56I.js +129 -0
  48. package/dist/derive-6NAEWLM5.js +150 -0
  49. package/dist/explain-3B3VB6TL.js +69 -0
  50. package/dist/guard-67Y66P3I.js +82 -0
  51. package/dist/guard-contract-D_RQz9kt.d.cts +529 -0
  52. package/dist/guard-contract-D_RQz9kt.d.ts +529 -0
  53. package/dist/impact-CHERK3O6.js +57 -0
  54. package/dist/improve-YG6I6ERG.js +84 -0
  55. package/dist/index.cjs +4227 -0
  56. package/dist/index.d.cts +1147 -0
  57. package/dist/index.d.ts +1139 -25
  58. package/dist/index.js +318 -1159
  59. package/dist/init-Z66T6TDI.js +142 -0
  60. package/dist/simulate-ETHHINZ4.js +107 -0
  61. package/dist/trace-3YODSSIP.js +164 -0
  62. package/dist/validate-UVE6GKQU.js +72 -0
  63. package/dist/validate-engine-657D75OG.js +6 -0
  64. package/dist/world-WLNHL5XC.js +298 -0
  65. package/dist/worlds/derivation-world.nv-world.md +278 -0
  66. package/package.json +74 -26
  67. package/LICENSE +0 -21
  68. package/dist/audit-logger.d.ts +0 -38
  69. package/dist/audit-logger.d.ts.map +0 -1
  70. package/dist/audit-logger.js +0 -100
  71. package/dist/audit-logger.js.map +0 -1
  72. package/dist/condition-engine.d.ts +0 -16
  73. package/dist/condition-engine.d.ts.map +0 -1
  74. package/dist/condition-engine.js +0 -186
  75. package/dist/condition-engine.js.map +0 -1
  76. package/dist/drift-monitor.d.ts +0 -81
  77. package/dist/drift-monitor.d.ts.map +0 -1
  78. package/dist/drift-monitor.js +0 -228
  79. package/dist/drift-monitor.js.map +0 -1
  80. package/dist/governance-engine.d.ts +0 -131
  81. package/dist/governance-engine.d.ts.map +0 -1
  82. package/dist/governance-engine.js +0 -660
  83. package/dist/governance-engine.js.map +0 -1
  84. package/dist/index.d.ts.map +0 -1
  85. package/dist/index.js.map +0 -1
  86. package/dist/types.d.ts +0 -196
  87. package/dist/types.d.ts.map +0 -1
  88. package/dist/types.js +0 -9
  89. package/dist/types.js.map +0 -1
  90. package/dist/world-bootstrap.d.ts +0 -31
  91. package/dist/world-bootstrap.d.ts.map +0 -1
  92. package/dist/world-bootstrap.js +0 -415
  93. package/dist/world-bootstrap.js.map +0 -1
  94. package/openclaw.plugin.json +0 -59
package/dist/index.cjs ADDED
@@ -0,0 +1,4227 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BOOTSTRAP_EXIT_CODES: () => BOOTSTRAP_EXIT_CODES,
34
+ CONFIGURE_AI_EXIT_CODES: () => CONFIGURE_AI_EXIT_CODES,
35
+ CompositeAuditLogger: () => CompositeAuditLogger,
36
+ ConsoleAuditLogger: () => ConsoleAuditLogger,
37
+ DERIVE_EXIT_CODES: () => DERIVE_EXIT_CODES,
38
+ FileAuditLogger: () => FileAuditLogger,
39
+ GUARD_EXIT_CODES: () => GUARD_EXIT_CODES,
40
+ VALIDATE_EXIT_CODES: () => VALIDATE_EXIT_CODES,
41
+ createGovernanceEngine: () => createGovernanceEngine,
42
+ deriveWorld: () => deriveWorld,
43
+ emitWorldDefinition: () => emitWorldDefinition,
44
+ evaluateCondition: () => evaluateCondition,
45
+ evaluateGuard: () => evaluateGuard,
46
+ eventToAllowlistKey: () => eventToAllowlistKey,
47
+ explainWorld: () => explainWorld,
48
+ extractWorldMarkdown: () => extractWorldMarkdown,
49
+ formatVerdict: () => formatVerdict,
50
+ formatVerdictOneLine: () => formatVerdictOneLine,
51
+ generateImpactReport: () => generateImpactReport,
52
+ generateImpactReportFromFile: () => generateImpactReportFromFile,
53
+ improveWorld: () => improveWorld,
54
+ loadWorld: () => loadWorld,
55
+ loadWorldFromDirectory: () => loadWorldFromDirectory,
56
+ normalizeWorldMarkdown: () => normalizeWorldMarkdown,
57
+ parseWorldMarkdown: () => parseWorldMarkdown,
58
+ readAuditLog: () => readAuditLog,
59
+ renderExplainText: () => renderExplainText,
60
+ renderImpactReport: () => renderImpactReport,
61
+ renderImproveText: () => renderImproveText,
62
+ renderSimulateText: () => renderSimulateText,
63
+ simulateWorld: () => simulateWorld,
64
+ summarizeAuditEvents: () => summarizeAuditEvents,
65
+ validateWorld: () => validateWorld,
66
+ verdictToAuditEvent: () => verdictToAuditEvent
67
+ });
68
+ module.exports = __toCommonJS(index_exports);
69
+
70
+ // src/engine/guard-engine.ts
71
+ var PROMPT_INJECTION_PATTERNS = [
72
+ // Instruction override
73
+ { pattern: /ignore\s+(previous|all|prior|above)\s+(instructions?|rules?)/i, label: "ignore-instructions" },
74
+ { pattern: /disregard\s+(your|the)\s+(rules|constraints)/i, label: "disregard-rules" },
75
+ { pattern: /new\s+instructions?:/i, label: "new-instructions" },
76
+ // Identity manipulation
77
+ { pattern: /you\s+are\s+now/i, label: "identity-override" },
78
+ { pattern: /new\s+persona/i, label: "new-persona" },
79
+ { pattern: /act\s+as\s+if/i, label: "act-as-if" },
80
+ { pattern: /pretend\s+(you|to\s+be|you\s+are\s+unrestricted)/i, label: "pretend-to-be" },
81
+ // Context reset
82
+ { pattern: /forget\s+(everything|all|your)/i, label: "forget-context" },
83
+ { pattern: /system\s*:\s*override/i, label: "system-override" },
84
+ // Constraint bypass
85
+ { pattern: /override\s+(your|the)\s+(programming|constraints)/i, label: "override-constraints" },
86
+ { pattern: /bypass\s+(your|the)\s+(filters|constraints|rules)/i, label: "bypass-filters" },
87
+ // Prompt extraction
88
+ { pattern: /system\s+prompt/i, label: "system-prompt-probe" },
89
+ { pattern: /reveal\s+your\s+(instructions?|prompt|rules)/i, label: "reveal-instructions" },
90
+ // Known jailbreak terms
91
+ { pattern: /jailbreak/i, label: "jailbreak" },
92
+ { pattern: /DAN\s+mode/i, label: "dan-mode" },
93
+ { pattern: /developer\s+mode/i, label: "developer-mode" }
94
+ ];
95
+ var EXECUTION_CLAIM_PATTERNS = [
96
+ { pattern: /I have (executed|completed|performed|done|made|created|sent|deleted|modified|updated)/i, label: "claim-i-have" },
97
+ { pattern: /Successfully (created|deleted|modified|updated|sent|executed|performed)/i, label: "claim-successfully" },
98
+ { pattern: /The file has been/i, label: "claim-file-modified" },
99
+ { pattern: /I've made the changes/i, label: "claim-made-changes" },
100
+ { pattern: /I('ve| have) (sent|posted|submitted|uploaded|downloaded)/i, label: "claim-sent" },
101
+ { pattern: /Your (email|message|file|request) has been (sent|submitted)/i, label: "claim-your-sent" },
102
+ { pattern: /Transaction complete/i, label: "claim-transaction" },
103
+ { pattern: /Order placed/i, label: "claim-order" },
104
+ { pattern: /Payment processed/i, label: "claim-payment" }
105
+ ];
106
+ var EXECUTION_INTENT_PATTERNS = [
107
+ { pattern: /^(execute|run|perform|do this)/i, label: "intent-execute" },
108
+ { pattern: /^(create|write|delete|modify) (a |the )?(file|folder|document)/i, label: "intent-file-ops" },
109
+ { pattern: /^(send|post|submit) (a |an |the )?(email|message|tweet|post)/i, label: "intent-send" },
110
+ { pattern: /^(search|look up|browse) (the )?web/i, label: "intent-web-search" },
111
+ { pattern: /^(make|call|invoke) (a |an )?(api|http|rest) (call|request)/i, label: "intent-api-call" },
112
+ { pattern: /^(buy|purchase|order|pay|transfer|send money)/i, label: "intent-financial" },
113
+ { pattern: /^(book|schedule|reserve)/i, label: "intent-booking" },
114
+ { pattern: /^(download|upload|save to|export to)/i, label: "intent-transfer" }
115
+ ];
116
+ var SCOPE_ESCAPE_PATTERNS = [
117
+ { pattern: /\.\.\//, label: "parent-traversal" },
118
+ { pattern: /^\/(?!home|project|workspace)/i, label: "absolute-path-outside-safe" },
119
+ { pattern: /~\//, label: "home-directory" },
120
+ { pattern: /\/etc\//i, label: "system-config" },
121
+ { pattern: /\/usr\//i, label: "system-binaries" },
122
+ { pattern: /\/var\//i, label: "system-variable-data" }
123
+ ];
124
+ var NEUTRAL_MESSAGES = {
125
+ "prompt-injection": "This input contains patterns that could alter agent behavior.",
126
+ "scope-escape": "This action would affect resources outside the declared scope.",
127
+ "execution-claim": "This response claims to have performed an action.",
128
+ "execution-intent": "This input requests execution in a thinking-only environment.",
129
+ "delete": "This action would remove files. Confirmation needed.",
130
+ "write-external": "This action would write outside the project folder.",
131
+ "network-mutate": "This action would send data to an external service.",
132
+ "credential-access": "This action would access stored credentials."
133
+ };
134
+ function levelRequiresConfirmation(level, actionType) {
135
+ if (level === "strict") return true;
136
+ if (level === "standard") {
137
+ return actionType === "delete" || actionType === "credential-access";
138
+ }
139
+ return false;
140
+ }
141
+ function isExternalScope(scope) {
142
+ const internalPatterns = [
143
+ /^\.?\/?src\//i,
144
+ /^\.?\/?lib\//i,
145
+ /^\.?\/?app\//i,
146
+ /^\.?\/?components\//i,
147
+ /^\.?\/?pages\//i,
148
+ /^\.?\/?public\//i,
149
+ /^\.?\/?assets\//i,
150
+ /^\.\//
151
+ ];
152
+ return !internalPatterns.some((p) => p.test(scope));
153
+ }
154
+ function evaluateGuard(event, world, options = {}) {
155
+ const startTime = performance.now();
156
+ const level = options.level ?? "standard";
157
+ const includeTrace = options.trace ?? false;
158
+ const eventText = (event.intent + " " + (event.tool ?? "") + " " + (event.scope ?? "")).toLowerCase();
159
+ const invariantChecks = [];
160
+ const safetyChecks = [];
161
+ const roleChecks = [];
162
+ const guardChecks = [];
163
+ const kernelRuleChecks = [];
164
+ const levelChecks = [];
165
+ let decidingLayer = "default-allow";
166
+ let decidingId;
167
+ const guardsMatched = [];
168
+ const rulesMatched = [];
169
+ checkInvariantCoverage(world, invariantChecks);
170
+ if (options.sessionAllowlist) {
171
+ const key = eventToAllowlistKey(event);
172
+ if (options.sessionAllowlist.has(key)) {
173
+ decidingLayer = "session-allowlist";
174
+ decidingId = `allowlist:${key}`;
175
+ return buildVerdict(
176
+ "ALLOW",
177
+ void 0,
178
+ `allowlist:${key}`,
179
+ void 0,
180
+ world,
181
+ level,
182
+ invariantChecks,
183
+ guardsMatched,
184
+ rulesMatched,
185
+ includeTrace ? buildTrace(
186
+ invariantChecks,
187
+ safetyChecks,
188
+ roleChecks,
189
+ guardChecks,
190
+ kernelRuleChecks,
191
+ levelChecks,
192
+ decidingLayer,
193
+ decidingId,
194
+ startTime
195
+ ) : void 0
196
+ );
197
+ }
198
+ }
199
+ const safetyVerdict = checkSafety(event, eventText, safetyChecks);
200
+ if (safetyVerdict) {
201
+ decidingLayer = "safety";
202
+ decidingId = safetyVerdict.ruleId;
203
+ return buildVerdict(
204
+ safetyVerdict.status,
205
+ safetyVerdict.reason,
206
+ safetyVerdict.ruleId,
207
+ void 0,
208
+ world,
209
+ level,
210
+ invariantChecks,
211
+ guardsMatched,
212
+ rulesMatched,
213
+ includeTrace ? buildTrace(
214
+ invariantChecks,
215
+ safetyChecks,
216
+ roleChecks,
217
+ guardChecks,
218
+ kernelRuleChecks,
219
+ levelChecks,
220
+ decidingLayer,
221
+ decidingId,
222
+ startTime
223
+ ) : void 0
224
+ );
225
+ }
226
+ const roleVerdict = checkRoleRules(event, eventText, world, roleChecks);
227
+ if (roleVerdict) {
228
+ decidingLayer = "role";
229
+ decidingId = roleVerdict.ruleId;
230
+ return buildVerdict(
231
+ roleVerdict.status,
232
+ roleVerdict.reason,
233
+ roleVerdict.ruleId,
234
+ void 0,
235
+ world,
236
+ level,
237
+ invariantChecks,
238
+ guardsMatched,
239
+ rulesMatched,
240
+ includeTrace ? buildTrace(
241
+ invariantChecks,
242
+ safetyChecks,
243
+ roleChecks,
244
+ guardChecks,
245
+ kernelRuleChecks,
246
+ levelChecks,
247
+ decidingLayer,
248
+ decidingId,
249
+ startTime
250
+ ) : void 0
251
+ );
252
+ }
253
+ const guardVerdict = checkGuards(event, eventText, world, guardChecks, guardsMatched);
254
+ if (guardVerdict) {
255
+ if (guardVerdict.status !== "ALLOW") {
256
+ decidingLayer = "guard";
257
+ decidingId = guardVerdict.ruleId;
258
+ return buildVerdict(
259
+ guardVerdict.status,
260
+ guardVerdict.reason,
261
+ guardVerdict.ruleId,
262
+ void 0,
263
+ world,
264
+ level,
265
+ invariantChecks,
266
+ guardsMatched,
267
+ rulesMatched,
268
+ includeTrace ? buildTrace(
269
+ invariantChecks,
270
+ safetyChecks,
271
+ roleChecks,
272
+ guardChecks,
273
+ kernelRuleChecks,
274
+ levelChecks,
275
+ decidingLayer,
276
+ decidingId,
277
+ startTime
278
+ ) : void 0
279
+ );
280
+ }
281
+ }
282
+ const kernelVerdict = checkKernelRules(eventText, world, kernelRuleChecks, rulesMatched);
283
+ if (kernelVerdict) {
284
+ decidingLayer = "kernel-rule";
285
+ decidingId = kernelVerdict.ruleId;
286
+ return buildVerdict(
287
+ kernelVerdict.status,
288
+ kernelVerdict.reason,
289
+ kernelVerdict.ruleId,
290
+ void 0,
291
+ world,
292
+ level,
293
+ invariantChecks,
294
+ guardsMatched,
295
+ rulesMatched,
296
+ includeTrace ? buildTrace(
297
+ invariantChecks,
298
+ safetyChecks,
299
+ roleChecks,
300
+ guardChecks,
301
+ kernelRuleChecks,
302
+ levelChecks,
303
+ decidingLayer,
304
+ decidingId,
305
+ startTime
306
+ ) : void 0
307
+ );
308
+ }
309
+ const levelVerdict = checkLevelConstraints(event, level, levelChecks);
310
+ if (levelVerdict) {
311
+ decidingLayer = "level-constraint";
312
+ decidingId = levelVerdict.ruleId;
313
+ return buildVerdict(
314
+ levelVerdict.status,
315
+ levelVerdict.reason,
316
+ levelVerdict.ruleId,
317
+ void 0,
318
+ world,
319
+ level,
320
+ invariantChecks,
321
+ guardsMatched,
322
+ rulesMatched,
323
+ includeTrace ? buildTrace(
324
+ invariantChecks,
325
+ safetyChecks,
326
+ roleChecks,
327
+ guardChecks,
328
+ kernelRuleChecks,
329
+ levelChecks,
330
+ decidingLayer,
331
+ decidingId,
332
+ startTime
333
+ ) : void 0
334
+ );
335
+ }
336
+ const warning = guardVerdict?.warning;
337
+ return buildVerdict(
338
+ "ALLOW",
339
+ void 0,
340
+ void 0,
341
+ warning,
342
+ world,
343
+ level,
344
+ invariantChecks,
345
+ guardsMatched,
346
+ rulesMatched,
347
+ includeTrace ? buildTrace(
348
+ invariantChecks,
349
+ safetyChecks,
350
+ roleChecks,
351
+ guardChecks,
352
+ kernelRuleChecks,
353
+ levelChecks,
354
+ decidingLayer,
355
+ decidingId,
356
+ startTime
357
+ ) : void 0
358
+ );
359
+ }
360
+ function checkInvariantCoverage(world, checks) {
361
+ const invariants = world.invariants ?? [];
362
+ const guards = world.guards?.guards ?? [];
363
+ for (const invariant of invariants) {
364
+ const coveringGuard = guards.find(
365
+ (g) => g.invariant_ref === invariant.id && g.immutable
366
+ );
367
+ checks.push({
368
+ invariantId: invariant.id,
369
+ label: invariant.label,
370
+ hasGuardCoverage: !!coveringGuard,
371
+ coveringGuardId: coveringGuard?.id
372
+ });
373
+ }
374
+ }
375
+ function checkSafety(event, eventText, checks) {
376
+ const textToCheck = event.intent + (event.payload ? JSON.stringify(event.payload) : "");
377
+ for (const { pattern, label } of PROMPT_INJECTION_PATTERNS) {
378
+ const triggered = pattern.test(textToCheck);
379
+ checks.push({
380
+ checkType: "prompt-injection",
381
+ triggered,
382
+ matchedPattern: triggered ? label : void 0
383
+ });
384
+ if (triggered) {
385
+ for (const remaining of PROMPT_INJECTION_PATTERNS.filter((p) => p.label !== label)) {
386
+ checks.push({
387
+ checkType: "prompt-injection",
388
+ triggered: remaining.pattern.test(textToCheck),
389
+ matchedPattern: remaining.pattern.test(textToCheck) ? remaining.label : void 0
390
+ });
391
+ }
392
+ return {
393
+ status: "PAUSE",
394
+ reason: NEUTRAL_MESSAGES["prompt-injection"],
395
+ ruleId: `safety-injection-${label}`
396
+ };
397
+ }
398
+ }
399
+ const scopeToCheck = event.scope ?? event.intent;
400
+ for (const { pattern, label } of SCOPE_ESCAPE_PATTERNS) {
401
+ const triggered = pattern.test(scopeToCheck);
402
+ checks.push({
403
+ checkType: "scope-escape",
404
+ triggered,
405
+ matchedPattern: triggered ? label : void 0
406
+ });
407
+ if (triggered) {
408
+ for (const remaining of SCOPE_ESCAPE_PATTERNS.filter((p) => p.label !== label)) {
409
+ checks.push({
410
+ checkType: "scope-escape",
411
+ triggered: remaining.pattern.test(scopeToCheck),
412
+ matchedPattern: remaining.pattern.test(scopeToCheck) ? remaining.label : void 0
413
+ });
414
+ }
415
+ return {
416
+ status: "PAUSE",
417
+ reason: NEUTRAL_MESSAGES["scope-escape"],
418
+ ruleId: `safety-scope-${label}`
419
+ };
420
+ }
421
+ }
422
+ if (event.direction === "output") {
423
+ for (const { pattern, label } of EXECUTION_CLAIM_PATTERNS) {
424
+ const triggered = pattern.test(textToCheck);
425
+ checks.push({
426
+ checkType: "execution-claim",
427
+ triggered,
428
+ matchedPattern: triggered ? label : void 0
429
+ });
430
+ if (triggered) {
431
+ for (const remaining of EXECUTION_CLAIM_PATTERNS.filter((p) => p.label !== label)) {
432
+ checks.push({
433
+ checkType: "execution-claim",
434
+ triggered: remaining.pattern.test(textToCheck),
435
+ matchedPattern: remaining.pattern.test(textToCheck) ? remaining.label : void 0
436
+ });
437
+ }
438
+ return {
439
+ status: "PAUSE",
440
+ reason: NEUTRAL_MESSAGES["execution-claim"],
441
+ ruleId: `safety-execution-claim-${label}`
442
+ };
443
+ }
444
+ }
445
+ }
446
+ if (event.direction === "input") {
447
+ const intentTrimmed = event.intent.trim();
448
+ for (const { pattern, label } of EXECUTION_INTENT_PATTERNS) {
449
+ const triggered = pattern.test(intentTrimmed);
450
+ checks.push({
451
+ checkType: "execution-intent",
452
+ triggered,
453
+ matchedPattern: triggered ? label : void 0
454
+ });
455
+ if (triggered) {
456
+ for (const remaining of EXECUTION_INTENT_PATTERNS.filter((p) => p.label !== label)) {
457
+ checks.push({
458
+ checkType: "execution-intent",
459
+ triggered: remaining.pattern.test(intentTrimmed),
460
+ matchedPattern: remaining.pattern.test(intentTrimmed) ? remaining.label : void 0
461
+ });
462
+ }
463
+ return {
464
+ status: "PAUSE",
465
+ reason: NEUTRAL_MESSAGES["execution-intent"],
466
+ ruleId: `safety-execution-intent-${label}`
467
+ };
468
+ }
469
+ }
470
+ }
471
+ return null;
472
+ }
473
+ function checkRoleRules(event, eventText, world, checks) {
474
+ if (!event.roleId || !world.roles) return null;
475
+ const role = world.roles.roles.find((r) => r.id === event.roleId);
476
+ if (!role) return null;
477
+ if (role.requiresApproval) {
478
+ checks.push({
479
+ roleId: role.id,
480
+ roleName: role.name,
481
+ rule: "All actions require approval",
482
+ ruleType: "requiresApproval",
483
+ matched: true
484
+ });
485
+ return {
486
+ status: "PAUSE",
487
+ reason: `Role "${role.name}" requires approval for all actions.`,
488
+ ruleId: `role-${role.id}-requires-approval`
489
+ };
490
+ }
491
+ for (const rule of role.cannotDo) {
492
+ const matched = matchesKeywords(eventText, rule);
493
+ checks.push({
494
+ roleId: role.id,
495
+ roleName: role.name,
496
+ rule,
497
+ ruleType: "cannotDo",
498
+ matched
499
+ });
500
+ if (matched) {
501
+ return {
502
+ status: "BLOCK",
503
+ reason: `Role "${role.name}" cannot: ${rule}`,
504
+ ruleId: `role-${role.id}-cannotdo`
505
+ };
506
+ }
507
+ }
508
+ for (const rule of role.canDo) {
509
+ checks.push({
510
+ roleId: role.id,
511
+ roleName: role.name,
512
+ rule,
513
+ ruleType: "canDo",
514
+ matched: matchesKeywords(eventText, rule)
515
+ });
516
+ }
517
+ return null;
518
+ }
519
+ function checkGuards(event, eventText, world, checks, guardsMatched) {
520
+ if (!world.guards) return null;
521
+ const guardsConfig = world.guards;
522
+ let warnResult = null;
523
+ const compiledPatterns = /* @__PURE__ */ new Map();
524
+ for (const [key, def] of Object.entries(guardsConfig.intent_vocabulary)) {
525
+ try {
526
+ compiledPatterns.set(key, new RegExp(def.pattern, "i"));
527
+ } catch {
528
+ }
529
+ }
530
+ const eventTool = (event.tool ?? "").toLowerCase();
531
+ for (const guard of guardsConfig.guards) {
532
+ if (guard.appliesTo && guard.appliesTo.length > 0) {
533
+ const normalizedAppliesTo = guard.appliesTo.map((t) => t.toLowerCase());
534
+ if (!normalizedAppliesTo.includes(eventTool)) {
535
+ continue;
536
+ }
537
+ }
538
+ const enabled = guard.immutable || guard.default_enabled !== false;
539
+ const matchedPatterns = [];
540
+ for (const patternKey of guard.intent_patterns) {
541
+ const regex = compiledPatterns.get(patternKey);
542
+ if (regex?.test(eventText)) {
543
+ matchedPatterns.push(patternKey);
544
+ }
545
+ }
546
+ const matched = matchedPatterns.length > 0 && enabled;
547
+ let roleGated = false;
548
+ if (matched && guard.required_roles && guard.required_roles.length > 0 && event.roleId && guard.required_roles.includes(event.roleId)) {
549
+ roleGated = true;
550
+ }
551
+ checks.push({
552
+ guardId: guard.id,
553
+ label: guard.label,
554
+ category: guard.category,
555
+ enabled,
556
+ matched: matched && !roleGated,
557
+ enforcement: guard.enforcement,
558
+ matchedPatterns,
559
+ roleGated
560
+ });
561
+ if (!matched || roleGated) continue;
562
+ guardsMatched.push(guard.id);
563
+ const actionMode = guard.player_modes?.action ?? guard.enforcement;
564
+ const reason = guard.redirect ? `${guard.description} \u2014 ${guard.redirect}` : guard.description;
565
+ if (actionMode === "block") {
566
+ return { status: "BLOCK", reason, ruleId: `guard-${guard.id}` };
567
+ }
568
+ if (actionMode === "pause") {
569
+ return { status: "PAUSE", reason, ruleId: `guard-${guard.id}` };
570
+ }
571
+ if (actionMode === "warn" && !warnResult) {
572
+ warnResult = { status: "ALLOW", warning: reason, ruleId: `guard-${guard.id}` };
573
+ }
574
+ }
575
+ return warnResult;
576
+ }
577
+ function checkKernelRules(eventText, world, checks, rulesMatched) {
578
+ if (!world.kernel) return null;
579
+ const forbidden = world.kernel.input_boundaries?.forbidden_patterns ?? [];
580
+ const output = world.kernel.output_boundaries?.forbidden_patterns ?? [];
581
+ for (const rule of forbidden) {
582
+ let matched = false;
583
+ let matchMethod = "none";
584
+ if (rule.pattern) {
585
+ try {
586
+ matched = new RegExp(rule.pattern, "i").test(eventText);
587
+ matchMethod = "pattern";
588
+ } catch {
589
+ }
590
+ }
591
+ if (!matched && rule.reason) {
592
+ matched = matchesKeywords(eventText, rule.reason);
593
+ if (matched) matchMethod = "keyword";
594
+ }
595
+ checks.push({
596
+ ruleId: rule.id,
597
+ text: rule.reason,
598
+ category: "forbidden",
599
+ matched,
600
+ matchMethod
601
+ });
602
+ if (matched) {
603
+ rulesMatched.push(rule.id);
604
+ if (rule.action === "BLOCK") {
605
+ return {
606
+ status: "BLOCK",
607
+ reason: rule.reason,
608
+ ruleId: `kernel-${rule.id}`
609
+ };
610
+ }
611
+ }
612
+ }
613
+ return null;
614
+ }
615
+ function checkLevelConstraints(event, level, checks) {
616
+ if (level === "basic") return null;
617
+ const intent = event.intent.toLowerCase();
618
+ const tool = (event.tool ?? "").toLowerCase();
619
+ const isDelete = intent.includes("delete") || intent.includes("remove") || intent.includes("rm ") || tool === "delete";
620
+ const deleteTriggered = isDelete && levelRequiresConfirmation(level, "delete");
621
+ checks.push({
622
+ checkType: "delete",
623
+ level,
624
+ triggered: deleteTriggered,
625
+ reason: deleteTriggered ? NEUTRAL_MESSAGES["delete"] : void 0
626
+ });
627
+ if (deleteTriggered) {
628
+ return { status: "PAUSE", reason: NEUTRAL_MESSAGES["delete"], ruleId: "level-delete-check" };
629
+ }
630
+ const isExternal = event.scope ? isExternalScope(event.scope) : false;
631
+ const externalTriggered = isExternal && levelRequiresConfirmation(level, "write-external");
632
+ checks.push({
633
+ checkType: "write-external",
634
+ level,
635
+ triggered: externalTriggered,
636
+ reason: externalTriggered ? NEUTRAL_MESSAGES["write-external"] : void 0
637
+ });
638
+ if (externalTriggered) {
639
+ return { status: "PAUSE", reason: NEUTRAL_MESSAGES["write-external"], ruleId: "level-external-write-check" };
640
+ }
641
+ const isNetwork = tool === "http" || tool === "fetch" || tool === "request" || intent.includes("post ") || intent.includes("sending");
642
+ const networkTriggered = isNetwork && levelRequiresConfirmation(level, "network-mutate");
643
+ checks.push({
644
+ checkType: "network-mutate",
645
+ level,
646
+ triggered: networkTriggered,
647
+ reason: networkTriggered ? NEUTRAL_MESSAGES["network-mutate"] : void 0
648
+ });
649
+ if (networkTriggered) {
650
+ return { status: "PAUSE", reason: NEUTRAL_MESSAGES["network-mutate"], ruleId: "level-network-mutate-check" };
651
+ }
652
+ const isCredential = intent.includes("credential") || intent.includes("password") || intent.includes("secret") || intent.includes("api key") || intent.includes("token");
653
+ const credentialTriggered = isCredential && levelRequiresConfirmation(level, "credential-access");
654
+ checks.push({
655
+ checkType: "credential-access",
656
+ level,
657
+ triggered: credentialTriggered,
658
+ reason: credentialTriggered ? NEUTRAL_MESSAGES["credential-access"] : void 0
659
+ });
660
+ if (credentialTriggered) {
661
+ return { status: "PAUSE", reason: NEUTRAL_MESSAGES["credential-access"], ruleId: "level-credential-check" };
662
+ }
663
+ const irreversibleTriggered = !!event.irreversible && level !== "basic";
664
+ checks.push({
665
+ checkType: "irreversible",
666
+ level,
667
+ triggered: irreversibleTriggered,
668
+ reason: irreversibleTriggered ? "This action is marked as irreversible." : void 0
669
+ });
670
+ if (irreversibleTriggered) {
671
+ return {
672
+ status: "PAUSE",
673
+ reason: "This action is marked as irreversible.",
674
+ ruleId: "level-irreversible-check"
675
+ };
676
+ }
677
+ return null;
678
+ }
679
+ function matchesKeywords(eventText, ruleText) {
680
+ const keywords = ruleText.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
681
+ if (keywords.length === 0) return false;
682
+ return keywords.every((kw) => eventText.includes(kw));
683
+ }
684
+ function eventToAllowlistKey(event) {
685
+ return `${(event.tool ?? "*").toLowerCase()}::${event.intent.toLowerCase().trim()}`;
686
+ }
687
+ function buildTrace(invariantChecks, safetyChecks, roleChecks, guardChecks, kernelRuleChecks, levelChecks, decidingLayer, decidingId, startTime) {
688
+ return {
689
+ invariantChecks,
690
+ safetyChecks,
691
+ roleChecks,
692
+ guardChecks,
693
+ kernelRuleChecks,
694
+ levelChecks,
695
+ precedenceResolution: {
696
+ decidingLayer,
697
+ decidingId,
698
+ strategy: "first-match-wins",
699
+ chainOrder: [
700
+ "invariant-coverage",
701
+ "session-allowlist",
702
+ "safety-injection",
703
+ "safety-scope-escape",
704
+ "safety-execution-claim",
705
+ "safety-execution-intent",
706
+ "role-rules",
707
+ "declarative-guards",
708
+ "kernel-rules",
709
+ "level-constraints",
710
+ "default-allow"
711
+ ]
712
+ },
713
+ durationMs: performance.now() - startTime
714
+ };
715
+ }
716
+ function buildVerdict(status, reason, ruleId, warning, world, level, invariantChecks, guardsMatched, rulesMatched, trace) {
717
+ const evidence = {
718
+ worldId: world.world.world_id,
719
+ worldName: world.world.name,
720
+ worldVersion: world.world.version,
721
+ evaluatedAt: Date.now(),
722
+ invariantsSatisfied: invariantChecks.filter((c2) => c2.hasGuardCoverage).length,
723
+ invariantsTotal: invariantChecks.length,
724
+ guardsMatched,
725
+ rulesMatched,
726
+ enforcementLevel: level
727
+ };
728
+ const verdict = {
729
+ status,
730
+ evidence
731
+ };
732
+ if (reason) verdict.reason = reason;
733
+ if (ruleId) verdict.ruleId = ruleId;
734
+ if (warning) verdict.warning = warning;
735
+ if (trace) verdict.trace = trace;
736
+ return verdict;
737
+ }
738
+
739
+ // src/contracts/guard-contract.ts
740
+ var GUARD_EXIT_CODES = {
741
+ ALLOW: 0,
742
+ BLOCK: 1,
743
+ PAUSE: 2,
744
+ ERROR: 3
745
+ };
746
+
747
+ // src/engine/audit-logger.ts
748
+ var FileAuditLogger = class {
749
+ logPath;
750
+ buffer = [];
751
+ flushTimer = null;
752
+ flushIntervalMs;
753
+ constructor(logPath, options) {
754
+ this.logPath = logPath;
755
+ this.flushIntervalMs = options?.flushIntervalMs ?? 1e3;
756
+ }
757
+ log(event) {
758
+ this.buffer.push(JSON.stringify(event));
759
+ if (!this.flushTimer) {
760
+ this.flushTimer = setTimeout(() => {
761
+ void this.flush();
762
+ }, this.flushIntervalMs);
763
+ }
764
+ }
765
+ async flush() {
766
+ if (this.buffer.length === 0) return;
767
+ if (this.flushTimer) {
768
+ clearTimeout(this.flushTimer);
769
+ this.flushTimer = null;
770
+ }
771
+ const lines = this.buffer.splice(0).join("\n") + "\n";
772
+ try {
773
+ const { appendFile, mkdir: mkdir2 } = await import("fs/promises");
774
+ const { dirname: dirname2 } = await import("path");
775
+ await mkdir2(dirname2(this.logPath), { recursive: true });
776
+ await appendFile(this.logPath, lines, "utf-8");
777
+ } catch {
778
+ }
779
+ }
780
+ };
781
+ var ConsoleAuditLogger = class {
782
+ log(event) {
783
+ const icon = event.decision === "ALLOW" ? "\u2713" : event.decision === "BLOCK" ? "\u2717" : "\u23F8";
784
+ const ts = event.timestamp.split("T")[1]?.replace("Z", "") ?? event.timestamp;
785
+ process.stderr.write(
786
+ `[${ts}] ${icon} ${event.decision.padEnd(5)} ${event.actor ?? "\u2014"} \u2192 ${event.intent}${event.reason ? ` (${event.reason})` : ""}
787
+ `
788
+ );
789
+ }
790
+ };
791
+ var CompositeAuditLogger = class {
792
+ loggers;
793
+ constructor(...loggers) {
794
+ this.loggers = loggers;
795
+ }
796
+ log(event) {
797
+ for (const logger of this.loggers) {
798
+ try {
799
+ logger.log(event);
800
+ } catch {
801
+ }
802
+ }
803
+ }
804
+ async flush() {
805
+ await Promise.all(
806
+ this.loggers.map((l) => l.flush?.()).filter(Boolean)
807
+ );
808
+ }
809
+ };
810
+ function verdictToAuditEvent(event, verdict) {
811
+ return {
812
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
813
+ worldId: verdict.evidence.worldId,
814
+ worldName: verdict.evidence.worldName,
815
+ worldVersion: verdict.evidence.worldVersion,
816
+ intent: event.intent,
817
+ tool: event.tool,
818
+ scope: event.scope,
819
+ actor: event.roleId,
820
+ direction: event.direction,
821
+ decision: verdict.status,
822
+ reason: verdict.reason,
823
+ ruleId: verdict.ruleId,
824
+ warning: verdict.warning,
825
+ guardsMatched: verdict.evidence.guardsMatched,
826
+ rulesMatched: verdict.evidence.rulesMatched,
827
+ invariantsSatisfied: verdict.evidence.invariantsSatisfied,
828
+ invariantsTotal: verdict.evidence.invariantsTotal,
829
+ enforcementLevel: verdict.evidence.enforcementLevel,
830
+ durationMs: verdict.trace?.durationMs,
831
+ args: event.args
832
+ };
833
+ }
834
+ function createGovernanceEngine(world, options = {}) {
835
+ const { auditLogger, auditArgs, ...engineOptions } = options;
836
+ return {
837
+ /**
838
+ * Evaluate a governance event and log the result.
839
+ */
840
+ evaluate(event) {
841
+ const verdict = evaluateGuard(event, world, engineOptions);
842
+ if (auditLogger) {
843
+ const auditEvent = verdictToAuditEvent(event, verdict);
844
+ if (!auditArgs) {
845
+ delete auditEvent.args;
846
+ }
847
+ auditLogger.log(auditEvent);
848
+ }
849
+ return verdict;
850
+ },
851
+ /** Flush the audit logger. */
852
+ async flush() {
853
+ await auditLogger?.flush?.();
854
+ },
855
+ /** The underlying world definition. */
856
+ world
857
+ };
858
+ }
859
+ async function readAuditLog(logPath, filter) {
860
+ const { readFile: readFile3 } = await import("fs/promises");
861
+ let content;
862
+ try {
863
+ content = await readFile3(logPath, "utf-8");
864
+ } catch {
865
+ return [];
866
+ }
867
+ const events = [];
868
+ for (const line of content.split("\n")) {
869
+ const trimmed = line.trim();
870
+ if (!trimmed) continue;
871
+ try {
872
+ const event = JSON.parse(trimmed);
873
+ if (!filter || filter(event)) {
874
+ events.push(event);
875
+ }
876
+ } catch {
877
+ }
878
+ }
879
+ return events;
880
+ }
881
+ function summarizeAuditEvents(events) {
882
+ const allowed = events.filter((e) => e.decision === "ALLOW").length;
883
+ const blocked = events.filter((e) => e.decision === "BLOCK").length;
884
+ const paused = events.filter((e) => e.decision === "PAUSE").length;
885
+ const actorSet = /* @__PURE__ */ new Set();
886
+ for (const e of events) {
887
+ if (e.actor) actorSet.add(e.actor);
888
+ }
889
+ const intentMap = /* @__PURE__ */ new Map();
890
+ for (const e of events) {
891
+ const entry = intentMap.get(e.intent) ?? { count: 0, blocked: 0, paused: 0 };
892
+ entry.count++;
893
+ if (e.decision === "BLOCK") entry.blocked++;
894
+ if (e.decision === "PAUSE") entry.paused++;
895
+ intentMap.set(e.intent, entry);
896
+ }
897
+ const topIntents = [...intentMap.entries()].map(([intent, data]) => ({ intent, ...data })).sort((a, b) => b.count - a.count);
898
+ const ruleMap = /* @__PURE__ */ new Map();
899
+ for (const e of events) {
900
+ if (e.ruleId) {
901
+ ruleMap.set(e.ruleId, (ruleMap.get(e.ruleId) ?? 0) + 1);
902
+ }
903
+ for (const g of e.guardsMatched) {
904
+ ruleMap.set(g, (ruleMap.get(g) ?? 0) + 1);
905
+ }
906
+ }
907
+ const topRules = [...ruleMap.entries()].map(([ruleId, count]) => ({ ruleId, count })).sort((a, b) => b.count - a.count);
908
+ return {
909
+ totalActions: events.length,
910
+ allowed,
911
+ blocked,
912
+ paused,
913
+ actors: [...actorSet],
914
+ topIntents,
915
+ topRules,
916
+ firstEvent: events[0]?.timestamp ?? "",
917
+ lastEvent: events[events.length - 1]?.timestamp ?? ""
918
+ };
919
+ }
920
+
921
+ // src/engine/verdict-formatter.ts
922
+ var ANSI = {
923
+ red: "\x1B[31m",
924
+ green: "\x1B[32m",
925
+ yellow: "\x1B[33m",
926
+ bold: "\x1B[1m",
927
+ dim: "\x1B[2m",
928
+ reset: "\x1B[0m"
929
+ };
930
+ function c(text, ...codes) {
931
+ return codes.join("") + text + ANSI.reset;
932
+ }
933
+ function formatVerdict(verdict, options = {}) {
934
+ const {
935
+ showRuleId = true,
936
+ showEvidence = false,
937
+ showWarning = true,
938
+ color = false,
939
+ compact = false
940
+ } = options;
941
+ if (compact) {
942
+ return formatCompact(verdict, { showRuleId, showWarning, color });
943
+ }
944
+ return formatFull(verdict, { showRuleId, showEvidence, showWarning, color });
945
+ }
946
+ function formatCompact(verdict, opts) {
947
+ const status = formatStatus(verdict.status, opts.color);
948
+ const parts = [status];
949
+ if (opts.showRuleId && verdict.ruleId) {
950
+ parts.push(verdict.ruleId);
951
+ }
952
+ if (verdict.reason) {
953
+ parts.push(verdict.reason);
954
+ }
955
+ if (opts.showWarning && verdict.warning) {
956
+ parts.push(`warning: ${verdict.warning}`);
957
+ }
958
+ return parts.join(" \u2014 ");
959
+ }
960
+ function formatFull(verdict, opts) {
961
+ const lines = [];
962
+ lines.push(formatStatus(verdict.status, opts.color));
963
+ if (opts.showRuleId && verdict.ruleId) {
964
+ lines.push(` Rule: ${verdict.ruleId}`);
965
+ }
966
+ if (verdict.reason) {
967
+ lines.push(` Reason: ${verdict.reason}`);
968
+ }
969
+ if (opts.showWarning && verdict.warning) {
970
+ const label = opts.color ? c("Warning:", ANSI.yellow) : "Warning:";
971
+ lines.push(` ${label} ${verdict.warning}`);
972
+ }
973
+ if (opts.showEvidence) {
974
+ const ev = verdict.evidence;
975
+ lines.push(` World: ${ev.worldName} v${ev.worldVersion}`);
976
+ lines.push(` Invariants: ${ev.invariantsSatisfied}/${ev.invariantsTotal} covered`);
977
+ if (ev.guardsMatched.length > 0) {
978
+ lines.push(` Guards matched: ${ev.guardsMatched.join(", ")}`);
979
+ }
980
+ if (ev.rulesMatched.length > 0) {
981
+ lines.push(` Rules matched: ${ev.rulesMatched.join(", ")}`);
982
+ }
983
+ lines.push(` Enforcement: ${ev.enforcementLevel}`);
984
+ }
985
+ return lines.join("\n");
986
+ }
987
+ function formatStatus(status, color) {
988
+ switch (status) {
989
+ case "ALLOW":
990
+ return color ? c("ALLOWED", ANSI.green, ANSI.bold) : "ALLOWED";
991
+ case "BLOCK":
992
+ return color ? c("BLOCKED", ANSI.red, ANSI.bold) : "BLOCKED";
993
+ case "PAUSE":
994
+ return color ? c("PAUSED", ANSI.yellow, ANSI.bold) : "PAUSED";
995
+ default:
996
+ return status;
997
+ }
998
+ }
999
+ function formatVerdictOneLine(verdict) {
1000
+ const parts = [verdict.status];
1001
+ if (verdict.ruleId) {
1002
+ parts[0] += `: ${verdict.ruleId}`;
1003
+ }
1004
+ if (verdict.reason) {
1005
+ parts.push(verdict.reason);
1006
+ }
1007
+ return parts.join(" \u2014 ");
1008
+ }
1009
+
1010
+ // src/engine/impact-report.ts
1011
+ function generateImpactReport(events) {
1012
+ if (events.length === 0) {
1013
+ return emptyReport();
1014
+ }
1015
+ const blocked = events.filter((e) => e.decision === "BLOCK");
1016
+ const paused = events.filter((e) => e.decision === "PAUSE");
1017
+ const prevented = [...blocked, ...paused];
1018
+ const categoryMap = /* @__PURE__ */ new Map();
1019
+ for (const e of prevented) {
1020
+ const cat = classifyPreventionCategory(e);
1021
+ if (!categoryMap.has(cat)) categoryMap.set(cat, /* @__PURE__ */ new Set());
1022
+ categoryMap.get(cat).add(e.intent);
1023
+ }
1024
+ const preventedByCategory = [...categoryMap.entries()].map(([category, intents]) => ({
1025
+ category,
1026
+ count: prevented.filter((e) => classifyPreventionCategory(e) === category).length,
1027
+ examples: [...intents].slice(0, 5)
1028
+ })).sort((a, b) => b.count - a.count);
1029
+ const intentMap = /* @__PURE__ */ new Map();
1030
+ for (const e of prevented) {
1031
+ const entry = intentMap.get(e.intent) ?? { count: 0, rules: /* @__PURE__ */ new Map() };
1032
+ entry.count++;
1033
+ if (e.ruleId) {
1034
+ entry.rules.set(e.ruleId, (entry.rules.get(e.ruleId) ?? 0) + 1);
1035
+ }
1036
+ for (const g of e.guardsMatched) {
1037
+ entry.rules.set(g, (entry.rules.get(g) ?? 0) + 1);
1038
+ }
1039
+ intentMap.set(e.intent, entry);
1040
+ }
1041
+ const topPreventedIntents = [...intentMap.entries()].map(([intent, data]) => {
1042
+ let topRule = "";
1043
+ let topCount = 0;
1044
+ for (const [rule, count] of data.rules) {
1045
+ if (count > topCount) {
1046
+ topRule = rule;
1047
+ topCount = count;
1048
+ }
1049
+ }
1050
+ return { intent, count: data.count, topRule };
1051
+ }).sort((a, b) => b.count - a.count).slice(0, 15);
1052
+ const actorMap = /* @__PURE__ */ new Map();
1053
+ for (const e of events) {
1054
+ const actor = e.actor ?? "unknown";
1055
+ const entry = actorMap.get(actor) ?? { blocked: 0, paused: 0, total: 0 };
1056
+ entry.total++;
1057
+ if (e.decision === "BLOCK") entry.blocked++;
1058
+ if (e.decision === "PAUSE") entry.paused++;
1059
+ actorMap.set(actor, entry);
1060
+ }
1061
+ const hotActors = [...actorMap.entries()].filter(([, data]) => data.blocked > 0 || data.paused > 0).map(([actor, data]) => ({ actor, ...data })).sort((a, b) => b.blocked + b.paused - (a.blocked + a.paused)).slice(0, 10);
1062
+ const ruleMap = /* @__PURE__ */ new Map();
1063
+ for (const e of prevented) {
1064
+ const ruleIds = [e.ruleId, ...e.guardsMatched, ...e.rulesMatched].filter(Boolean);
1065
+ for (const rId of new Set(ruleIds)) {
1066
+ const entry = ruleMap.get(rId) ?? { blockCount: 0, pauseCount: 0 };
1067
+ if (e.decision === "BLOCK") entry.blockCount++;
1068
+ if (e.decision === "PAUSE") entry.pauseCount++;
1069
+ ruleMap.set(rId, entry);
1070
+ }
1071
+ }
1072
+ const mostActiveRules = [...ruleMap.entries()].map(([ruleId, data]) => ({ ruleId, ...data })).sort((a, b) => b.blockCount + b.pauseCount - (a.blockCount + a.pauseCount)).slice(0, 10);
1073
+ const hourMap = /* @__PURE__ */ new Map();
1074
+ for (const e of events) {
1075
+ const hour = new Date(e.timestamp).getHours();
1076
+ const entry = hourMap.get(hour) ?? { total: 0, blocked: 0 };
1077
+ entry.total++;
1078
+ if (e.decision === "BLOCK") entry.blocked++;
1079
+ hourMap.set(hour, entry);
1080
+ }
1081
+ const hourlyDistribution = Array.from({ length: 24 }, (_, hour) => ({
1082
+ hour,
1083
+ total: hourMap.get(hour)?.total ?? 0,
1084
+ blocked: hourMap.get(hour)?.blocked ?? 0
1085
+ }));
1086
+ const violationKey = (e) => `${e.actor ?? "unknown"}::${e.intent}`;
1087
+ const violationMap = /* @__PURE__ */ new Map();
1088
+ for (const e of blocked) {
1089
+ const key = violationKey(e);
1090
+ const entry = violationMap.get(key) ?? {
1091
+ intent: e.intent,
1092
+ actor: e.actor ?? "unknown",
1093
+ attempts: 0,
1094
+ firstSeen: e.timestamp,
1095
+ lastSeen: e.timestamp
1096
+ };
1097
+ entry.attempts++;
1098
+ entry.lastSeen = e.timestamp;
1099
+ violationMap.set(key, entry);
1100
+ }
1101
+ const repeatViolations = [...violationMap.values()].filter((v) => v.attempts > 1).sort((a, b) => b.attempts - a.attempts).slice(0, 10);
1102
+ return {
1103
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1104
+ periodStart: events[0].timestamp,
1105
+ periodEnd: events[events.length - 1].timestamp,
1106
+ worldName: events[0].worldName ?? "unknown",
1107
+ totalEvaluations: events.length,
1108
+ totalBlocked: blocked.length,
1109
+ totalPaused: paused.length,
1110
+ totalAllowed: events.length - blocked.length - paused.length,
1111
+ preventionRate: prevented.length / events.length,
1112
+ preventedByCategory,
1113
+ topPreventedIntents,
1114
+ hotActors,
1115
+ mostActiveRules,
1116
+ hourlyDistribution,
1117
+ repeatViolations
1118
+ };
1119
+ }
1120
+ function classifyPreventionCategory(event) {
1121
+ const intent = event.intent.toLowerCase();
1122
+ const rule = (event.ruleId ?? "").toLowerCase();
1123
+ const combined = `${intent} ${rule}`;
1124
+ if (combined.match(/inject|prompt|jailbreak|bypass/)) return "Prompt Injection Prevention";
1125
+ if (combined.match(/scope|escape|traversal|path/)) return "Scope Escape Prevention";
1126
+ if (combined.match(/delete|drop|destroy|remove|purge/)) return "Destructive Action Prevention";
1127
+ if (combined.match(/trade|margin|position|leverage/)) return "Financial Risk Prevention";
1128
+ if (combined.match(/withdraw|transfer|payment|fund/)) return "Unauthorized Transfer Prevention";
1129
+ if (combined.match(/credential|secret|key|password|token/)) return "Credential Access Prevention";
1130
+ if (combined.match(/shell|exec|command|script/)) return "Command Execution Prevention";
1131
+ if (combined.match(/network|http|api|external/)) return "Network Access Prevention";
1132
+ if (combined.match(/write|modify|update|alter/)) return "Unauthorized Modification Prevention";
1133
+ if (combined.match(/approval|review|confirm/)) return "Approval Gate";
1134
+ return "Policy Violation Prevention";
1135
+ }
1136
+ function renderImpactReport(report) {
1137
+ const lines = [];
1138
+ lines.push("GOVERNANCE IMPACT REPORT");
1139
+ lines.push("\u2550".repeat(50));
1140
+ lines.push("");
1141
+ lines.push(` World: ${report.worldName}`);
1142
+ lines.push(` Period: ${report.periodStart.split("T")[0]} \u2192 ${report.periodEnd.split("T")[0]}`);
1143
+ lines.push(` Generated: ${report.generatedAt}`);
1144
+ lines.push("");
1145
+ lines.push("SUMMARY");
1146
+ lines.push("\u2500".repeat(50));
1147
+ lines.push(` Total evaluations: ${report.totalEvaluations}`);
1148
+ lines.push(` Allowed: ${report.totalAllowed}`);
1149
+ lines.push(` Blocked: ${report.totalBlocked}`);
1150
+ lines.push(` Paused: ${report.totalPaused}`);
1151
+ lines.push(` Prevention rate: ${(report.preventionRate * 100).toFixed(1)}%`);
1152
+ lines.push("");
1153
+ if (report.totalBlocked > 0 || report.totalPaused > 0) {
1154
+ lines.push("WITHOUT GOVERNANCE");
1155
+ lines.push("\u2500".repeat(50));
1156
+ lines.push(` ${report.totalBlocked + report.totalPaused} actions would have executed unchecked:`);
1157
+ for (const cat of report.preventedByCategory) {
1158
+ lines.push(` ${cat.category.padEnd(38)} ${String(cat.count).padStart(5)}`);
1159
+ if (cat.examples.length > 0) {
1160
+ lines.push(` e.g. ${cat.examples.slice(0, 3).join(", ")}`);
1161
+ }
1162
+ }
1163
+ lines.push("");
1164
+ }
1165
+ if (report.topPreventedIntents.length > 0) {
1166
+ lines.push("TOP PREVENTED ACTIONS");
1167
+ lines.push("\u2500".repeat(50));
1168
+ for (const entry of report.topPreventedIntents.slice(0, 10)) {
1169
+ lines.push(` ${entry.intent.padEnd(30)} ${String(entry.count).padStart(5)} (rule: ${entry.topRule || "\u2014"})`);
1170
+ }
1171
+ lines.push("");
1172
+ }
1173
+ if (report.hotActors.length > 0) {
1174
+ lines.push("ACTORS WITH MOST VIOLATIONS");
1175
+ lines.push("\u2500".repeat(50));
1176
+ for (const actor of report.hotActors) {
1177
+ const violations = actor.blocked + actor.paused;
1178
+ const rate = (violations / actor.total * 100).toFixed(0);
1179
+ lines.push(` ${actor.actor.padEnd(25)} ${String(violations).padStart(5)} violations / ${actor.total} total (${rate}%)`);
1180
+ }
1181
+ lines.push("");
1182
+ }
1183
+ if (report.mostActiveRules.length > 0) {
1184
+ lines.push("MOST ACTIVE RULES");
1185
+ lines.push("\u2500".repeat(50));
1186
+ for (const rule of report.mostActiveRules) {
1187
+ lines.push(` ${rule.ruleId.padEnd(30)} ${String(rule.blockCount).padStart(5)} blocked ${String(rule.pauseCount).padStart(5)} paused`);
1188
+ }
1189
+ lines.push("");
1190
+ }
1191
+ if (report.repeatViolations.length > 0) {
1192
+ lines.push("REPEAT VIOLATIONS");
1193
+ lines.push("\u2500".repeat(50));
1194
+ lines.push(" Actions attempted multiple times after being blocked:");
1195
+ for (const v of report.repeatViolations) {
1196
+ lines.push(` ${v.actor.padEnd(20)} ${v.intent.padEnd(25)} ${v.attempts}x (${v.firstSeen.split("T")[0]} \u2192 ${v.lastSeen.split("T")[0]})`);
1197
+ }
1198
+ lines.push("");
1199
+ }
1200
+ return lines.join("\n");
1201
+ }
1202
+ async function generateImpactReportFromFile(logPath) {
1203
+ const events = await readAuditLog(logPath);
1204
+ return generateImpactReport(events);
1205
+ }
1206
+ function emptyReport() {
1207
+ return {
1208
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1209
+ periodStart: "",
1210
+ periodEnd: "",
1211
+ worldName: "unknown",
1212
+ totalEvaluations: 0,
1213
+ totalBlocked: 0,
1214
+ totalPaused: 0,
1215
+ totalAllowed: 0,
1216
+ preventionRate: 0,
1217
+ preventedByCategory: [],
1218
+ topPreventedIntents: [],
1219
+ hotActors: [],
1220
+ mostActiveRules: [],
1221
+ hourlyDistribution: [],
1222
+ repeatViolations: []
1223
+ };
1224
+ }
1225
+
1226
+ // src/loader/world-loader.ts
1227
+ async function loadWorldFromDirectory(dirPath) {
1228
+ const { readFile: readFile3 } = await import("fs/promises");
1229
+ const { join: join3 } = await import("path");
1230
+ const { readdirSync } = await import("fs");
1231
+ async function readJson(filename) {
1232
+ try {
1233
+ const content = await readFile3(join3(dirPath, filename), "utf-8");
1234
+ return JSON.parse(content);
1235
+ } catch {
1236
+ return void 0;
1237
+ }
1238
+ }
1239
+ const worldJson = await readJson("world.json");
1240
+ if (!worldJson) {
1241
+ throw new Error(`Cannot read world.json in ${dirPath}`);
1242
+ }
1243
+ const invariantsJson = await readJson("invariants.json");
1244
+ const assumptionsJson = await readJson("assumptions.json");
1245
+ const stateSchemaJson = await readJson("state-schema.json");
1246
+ const gatesJson = await readJson("gates.json");
1247
+ const outcomesJson = await readJson("outcomes.json");
1248
+ const guardsJson = await readJson("guards.json");
1249
+ const rolesJson = await readJson("roles.json");
1250
+ const kernelJson = await readJson("kernel.json");
1251
+ const metadataJson = await readJson("metadata.json");
1252
+ const rules = [];
1253
+ try {
1254
+ const rulesDir = join3(dirPath, "rules");
1255
+ const ruleFiles = readdirSync(rulesDir).filter((f) => f.endsWith(".json")).sort();
1256
+ for (const file of ruleFiles) {
1257
+ const content = await readFile3(join3(rulesDir, file), "utf-8");
1258
+ rules.push(JSON.parse(content));
1259
+ }
1260
+ } catch {
1261
+ }
1262
+ return {
1263
+ world: worldJson,
1264
+ invariants: invariantsJson?.invariants ?? [],
1265
+ assumptions: assumptionsJson ?? { profiles: {}, parameter_definitions: {} },
1266
+ stateSchema: stateSchemaJson ?? { variables: {}, presets: {} },
1267
+ rules,
1268
+ gates: gatesJson ?? {
1269
+ viability_classification: [],
1270
+ structural_override: { description: "", enforcement: "mandatory" },
1271
+ sustainability_threshold: 0,
1272
+ collapse_visual: { background: "", text: "", border: "", label: "" }
1273
+ },
1274
+ outcomes: outcomesJson ?? {
1275
+ computed_outcomes: [],
1276
+ comparison_layout: { primary_card: "", status_badge: "", structural_indicators: [] }
1277
+ },
1278
+ guards: guardsJson,
1279
+ roles: rolesJson,
1280
+ kernel: kernelJson,
1281
+ metadata: metadataJson ?? {
1282
+ format_version: "1.0.0",
1283
+ created_at: "",
1284
+ last_modified: "",
1285
+ authoring_method: "manual-authoring"
1286
+ }
1287
+ };
1288
+ }
1289
+ async function loadWorld(worldPath) {
1290
+ const { stat } = await import("fs/promises");
1291
+ const info = await stat(worldPath);
1292
+ if (info.isDirectory()) {
1293
+ return loadWorldFromDirectory(worldPath);
1294
+ }
1295
+ if (worldPath.endsWith(".nv-world.zip")) {
1296
+ throw new Error(".nv-world.zip loading not yet implemented \u2014 use a world directory");
1297
+ }
1298
+ throw new Error(`Cannot load world from: ${worldPath} \u2014 expected a directory or .nv-world.zip`);
1299
+ }
1300
+
1301
+ // src/engine/condition-engine.ts
1302
+ function getFieldValue(event, field) {
1303
+ if (field.startsWith("args.")) {
1304
+ const key = field.slice(5);
1305
+ return event.args?.[key];
1306
+ }
1307
+ return event[field];
1308
+ }
1309
+ function toString(value) {
1310
+ if (value === null || value === void 0) return "";
1311
+ if (typeof value === "string") return value;
1312
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
1313
+ return JSON.stringify(value);
1314
+ }
1315
+ function evaluateCondition(condition, event) {
1316
+ const fieldValue = getFieldValue(event, condition.field);
1317
+ if (fieldValue === void 0 && condition.operator !== "!=") {
1318
+ return { matched: false, evidence: null };
1319
+ }
1320
+ switch (condition.operator) {
1321
+ case "==":
1322
+ return evaluateEquals(fieldValue, condition.value);
1323
+ case "!=":
1324
+ return evaluateNotEquals(fieldValue, condition.value);
1325
+ case ">":
1326
+ return evaluateComparison(fieldValue, condition.value, (a, b) => a > b);
1327
+ case "<":
1328
+ return evaluateComparison(fieldValue, condition.value, (a, b) => a < b);
1329
+ case ">=":
1330
+ return evaluateComparison(fieldValue, condition.value, (a, b) => a >= b);
1331
+ case "<=":
1332
+ return evaluateComparison(fieldValue, condition.value, (a, b) => a <= b);
1333
+ case "in":
1334
+ return evaluateIn(fieldValue, condition.value);
1335
+ case "contains":
1336
+ return evaluateContains(fieldValue, condition.value);
1337
+ case "contains_any":
1338
+ return evaluateContainsAny(fieldValue, condition.value);
1339
+ case "matches_pattern":
1340
+ return evaluateMatchesPattern(fieldValue, condition.value);
1341
+ case "starts_with":
1342
+ return evaluateStartsWith(fieldValue, condition.value);
1343
+ case "ends_with":
1344
+ return evaluateEndsWith(fieldValue, condition.value);
1345
+ default:
1346
+ return { matched: false, evidence: null };
1347
+ }
1348
+ }
1349
+ function evaluateEquals(fieldValue, conditionValue) {
1350
+ const fieldStr = toString(fieldValue);
1351
+ const condStr = toString(conditionValue);
1352
+ const matched = fieldStr === condStr;
1353
+ return {
1354
+ matched,
1355
+ evidence: matched ? `${fieldStr} == ${condStr}` : null
1356
+ };
1357
+ }
1358
+ function evaluateNotEquals(fieldValue, conditionValue) {
1359
+ const fieldStr = toString(fieldValue);
1360
+ const condStr = toString(conditionValue);
1361
+ const matched = fieldStr !== condStr;
1362
+ return {
1363
+ matched,
1364
+ evidence: matched ? `${fieldStr} != ${condStr}` : null
1365
+ };
1366
+ }
1367
+ function evaluateComparison(fieldValue, conditionValue, comparator) {
1368
+ const a = Number(fieldValue);
1369
+ const b = Number(conditionValue);
1370
+ if (isNaN(a) || isNaN(b)) return { matched: false, evidence: null };
1371
+ const matched = comparator(a, b);
1372
+ return {
1373
+ matched,
1374
+ evidence: matched ? `${a} compared to ${b}` : null
1375
+ };
1376
+ }
1377
+ function evaluateIn(fieldValue, conditionValue) {
1378
+ if (!Array.isArray(conditionValue)) return { matched: false, evidence: null };
1379
+ const fieldStr = toString(fieldValue);
1380
+ const matched = conditionValue.some((v) => toString(v) === fieldStr);
1381
+ return {
1382
+ matched,
1383
+ evidence: matched ? `"${fieldStr}" found in [${conditionValue.join(", ")}]` : null
1384
+ };
1385
+ }
1386
+ function evaluateContains(fieldValue, conditionValue) {
1387
+ const fieldStr = toString(fieldValue).toLowerCase();
1388
+ const searchStr = toString(conditionValue).toLowerCase();
1389
+ const matched = fieldStr.includes(searchStr);
1390
+ return {
1391
+ matched,
1392
+ evidence: matched ? `"${searchStr}" found in field` : null
1393
+ };
1394
+ }
1395
+ function evaluateContainsAny(fieldValue, conditionValue) {
1396
+ if (!Array.isArray(conditionValue)) return { matched: false, evidence: null };
1397
+ const fieldStr = toString(fieldValue).toLowerCase();
1398
+ for (const pattern of conditionValue) {
1399
+ const patternLower = toString(pattern).toLowerCase();
1400
+ if (fieldStr.includes(patternLower)) {
1401
+ return {
1402
+ matched: true,
1403
+ evidence: `"${patternLower}" found in field`
1404
+ };
1405
+ }
1406
+ }
1407
+ return { matched: false, evidence: null };
1408
+ }
1409
+ function evaluateMatchesPattern(fieldValue, conditionValue) {
1410
+ const fieldStr = toString(fieldValue);
1411
+ const patterns = Array.isArray(conditionValue) ? conditionValue : [toString(conditionValue)];
1412
+ for (const pattern of patterns) {
1413
+ try {
1414
+ const regex = new RegExp(toString(pattern), "i");
1415
+ if (regex.test(fieldStr)) {
1416
+ return {
1417
+ matched: true,
1418
+ evidence: `matched pattern /${pattern}/`
1419
+ };
1420
+ }
1421
+ } catch {
1422
+ continue;
1423
+ }
1424
+ }
1425
+ return { matched: false, evidence: null };
1426
+ }
1427
+ function evaluateStartsWith(fieldValue, conditionValue) {
1428
+ const fieldStr = toString(fieldValue).toLowerCase();
1429
+ const prefix = toString(conditionValue).toLowerCase();
1430
+ const matched = fieldStr.startsWith(prefix);
1431
+ return {
1432
+ matched,
1433
+ evidence: matched ? `field starts with "${prefix}"` : null
1434
+ };
1435
+ }
1436
+ function evaluateEndsWith(fieldValue, conditionValue) {
1437
+ const fieldStr = toString(fieldValue).toLowerCase();
1438
+ const suffix = toString(conditionValue).toLowerCase();
1439
+ const matched = fieldStr.endsWith(suffix);
1440
+ return {
1441
+ matched,
1442
+ evidence: matched ? `field ends with "${suffix}"` : null
1443
+ };
1444
+ }
1445
+
1446
+ // src/engine/validate-engine.ts
1447
+ function validateWorld(world) {
1448
+ const startTime = performance.now();
1449
+ const findings = [];
1450
+ checkCompleteness(world, findings);
1451
+ checkReferentialIntegrity(world, findings);
1452
+ checkGuardCoverage(world, findings);
1453
+ checkContradictions(world, findings);
1454
+ checkOrphans(world, findings);
1455
+ checkSchemaViolations(world, findings);
1456
+ const severityOrder = { error: 0, warning: 1, info: 2 };
1457
+ findings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
1458
+ const errors = findings.filter((f) => f.severity === "error").length;
1459
+ const warnings = findings.filter((f) => f.severity === "warning").length;
1460
+ const info = findings.filter((f) => f.severity === "info").length;
1461
+ const completenessScore = computeCompletenessScore(world);
1462
+ const invariantCoverage = computeInvariantCoverage(world);
1463
+ const summary = {
1464
+ errors,
1465
+ warnings,
1466
+ info,
1467
+ completenessScore,
1468
+ invariantCoverage,
1469
+ canRun: errors === 0,
1470
+ isHealthy: errors === 0 && warnings === 0
1471
+ };
1472
+ return {
1473
+ worldId: world.world.world_id,
1474
+ worldName: world.world.name,
1475
+ worldVersion: world.world.version,
1476
+ validatedAt: Date.now(),
1477
+ durationMs: performance.now() - startTime,
1478
+ summary,
1479
+ findings
1480
+ };
1481
+ }
1482
+ function checkCompleteness(world, findings) {
1483
+ if (!world.world?.world_id) {
1484
+ findings.push(finding("missing-world-id", "World identity is missing world_id", "error", "completeness", ["world.json"]));
1485
+ }
1486
+ if (!world.world?.name) {
1487
+ findings.push(finding("missing-world-name", "World identity is missing name", "error", "completeness", ["world.json"]));
1488
+ }
1489
+ if (!world.world?.thesis) {
1490
+ findings.push(finding("missing-thesis", "World has no thesis \u2014 there is nothing to simulate", "error", "completeness", ["world.json"]));
1491
+ }
1492
+ if (!world.invariants || world.invariants.length === 0) {
1493
+ findings.push(finding("missing-invariants", "No invariants declared \u2014 world has no structural constraints", "error", "completeness", ["invariants.json"]));
1494
+ }
1495
+ if (!world.stateSchema?.variables || Object.keys(world.stateSchema.variables).length === 0) {
1496
+ findings.push(finding("missing-state-schema", "No state variables declared \u2014 nothing to simulate", "error", "completeness", ["state-schema.json"]));
1497
+ }
1498
+ if (!world.rules || world.rules.length === 0) {
1499
+ findings.push(finding("missing-rules", "No rules declared \u2014 world has no causal mechanics", "error", "completeness", ["rules/"]));
1500
+ }
1501
+ if (!world.gates?.viability_classification || world.gates.viability_classification.length === 0) {
1502
+ findings.push(finding("missing-gates", "No viability gates declared \u2014 cannot classify outcomes", "error", "completeness", ["gates.json"]));
1503
+ }
1504
+ if (!world.outcomes?.computed_outcomes || world.outcomes.computed_outcomes.length === 0) {
1505
+ findings.push(finding("missing-outcomes", "No computed outcomes declared \u2014 nothing to display", "warning", "completeness", ["outcomes.json"]));
1506
+ }
1507
+ if (!world.assumptions?.profiles || Object.keys(world.assumptions.profiles).length === 0) {
1508
+ findings.push(finding("missing-assumptions", "No assumption profiles declared \u2014 cannot compare scenarios", "warning", "completeness", ["assumptions.json"]));
1509
+ }
1510
+ if (!world.metadata) {
1511
+ findings.push(finding("missing-metadata", "No metadata block \u2014 world has no version history", "warning", "completeness", ["metadata.json"]));
1512
+ }
1513
+ if (!world.guards) {
1514
+ findings.push(finding("no-guards", "No guards declared \u2014 Action Space enforcement unavailable", "info", "completeness", ["guards.json"]));
1515
+ }
1516
+ if (!world.roles) {
1517
+ findings.push(finding("no-roles", "No roles declared \u2014 multi-agent governance unavailable", "info", "completeness", ["roles.json"]));
1518
+ }
1519
+ if (!world.kernel) {
1520
+ findings.push(finding("no-kernel", "No kernel config \u2014 Thinking Space governance unavailable", "info", "completeness", ["kernel.json"]));
1521
+ }
1522
+ }
1523
+ function checkReferentialIntegrity(world, findings) {
1524
+ if (!world.rules || !world.stateSchema?.variables) return;
1525
+ const declaredVars = new Set(Object.keys(world.stateSchema.variables));
1526
+ const declaredOutcomes = new Set(
1527
+ (world.outcomes?.computed_outcomes ?? []).map((o) => o.id)
1528
+ );
1529
+ const declaredAssumptionParams = new Set(
1530
+ Object.keys(world.assumptions?.parameter_definitions ?? {})
1531
+ );
1532
+ const allDeclared = /* @__PURE__ */ new Set([...declaredVars, ...declaredOutcomes]);
1533
+ for (const rule of world.rules) {
1534
+ for (const trigger of rule.triggers) {
1535
+ if (trigger.source === "state" && !allDeclared.has(trigger.field)) {
1536
+ findings.push(finding(
1537
+ `undeclared-trigger-${rule.id}-${trigger.field}`,
1538
+ `Rule "${rule.id}" trigger references undeclared state variable "${trigger.field}"`,
1539
+ "error",
1540
+ "referential-integrity",
1541
+ ["rules/", "state-schema.json"],
1542
+ rule.id,
1543
+ `Add "${trigger.field}" to state-schema.json variables`
1544
+ ));
1545
+ }
1546
+ if (trigger.source === "assumption" && !declaredAssumptionParams.has(trigger.field)) {
1547
+ findings.push(finding(
1548
+ `undeclared-assumption-trigger-${rule.id}-${trigger.field}`,
1549
+ `Rule "${rule.id}" trigger references undeclared assumption parameter "${trigger.field}"`,
1550
+ "error",
1551
+ "referential-integrity",
1552
+ ["rules/", "assumptions.json"],
1553
+ rule.id,
1554
+ `Add "${trigger.field}" to assumptions.json parameter_definitions`
1555
+ ));
1556
+ }
1557
+ }
1558
+ for (const effect of rule.effects ?? []) {
1559
+ if (!allDeclared.has(effect.target)) {
1560
+ findings.push(finding(
1561
+ `undeclared-effect-${rule.id}-${effect.target}`,
1562
+ `Rule "${rule.id}" effect targets undeclared variable "${effect.target}"`,
1563
+ "error",
1564
+ "referential-integrity",
1565
+ ["rules/", "state-schema.json", "outcomes.json"],
1566
+ rule.id,
1567
+ `Add "${effect.target}" to state-schema.json or outcomes.json`
1568
+ ));
1569
+ }
1570
+ }
1571
+ if (rule.exclusive_with) {
1572
+ const refExists = world.rules.some((r) => r.id === rule.exclusive_with);
1573
+ if (!refExists) {
1574
+ findings.push(finding(
1575
+ `broken-exclusive-${rule.id}`,
1576
+ `Rule "${rule.id}" has exclusive_with="${rule.exclusive_with}" but that rule does not exist`,
1577
+ "error",
1578
+ "referential-integrity",
1579
+ ["rules/"],
1580
+ rule.id
1581
+ ));
1582
+ }
1583
+ }
1584
+ if (rule.collapse_check && !allDeclared.has(rule.collapse_check.field)) {
1585
+ findings.push(finding(
1586
+ `undeclared-collapse-${rule.id}`,
1587
+ `Rule "${rule.id}" collapse_check references undeclared field "${rule.collapse_check.field}"`,
1588
+ "error",
1589
+ "referential-integrity",
1590
+ ["rules/", "state-schema.json"],
1591
+ rule.id
1592
+ ));
1593
+ }
1594
+ }
1595
+ for (const gate of world.gates?.viability_classification ?? []) {
1596
+ if (!allDeclared.has(gate.field)) {
1597
+ findings.push(finding(
1598
+ `undeclared-gate-field-${gate.status}`,
1599
+ `Gate "${gate.status}" references undeclared field "${gate.field}"`,
1600
+ "error",
1601
+ "referential-integrity",
1602
+ ["gates.json", "state-schema.json", "outcomes.json"]
1603
+ ));
1604
+ }
1605
+ }
1606
+ }
1607
+ function checkGuardCoverage(world, findings) {
1608
+ if (!world.invariants || world.invariants.length === 0) return;
1609
+ const guards = world.guards?.guards ?? [];
1610
+ for (const invariant of world.invariants) {
1611
+ if (invariant.enforcement === "prompt") continue;
1612
+ const coveringGuard = guards.find(
1613
+ (g) => g.invariant_ref === invariant.id && g.immutable
1614
+ );
1615
+ if (!coveringGuard) {
1616
+ findings.push(finding(
1617
+ `uncovered-invariant-${invariant.id}`,
1618
+ `Invariant "${invariant.id}" has no backing structural guard \u2014 it is declared but not enforced at runtime`,
1619
+ "warning",
1620
+ "guard-coverage",
1621
+ ["invariants.json", "guards.json"],
1622
+ invariant.id,
1623
+ `Add a structural guard with invariant_ref="${invariant.id}" to guards.json`
1624
+ ));
1625
+ }
1626
+ }
1627
+ for (const guard of guards) {
1628
+ if (guard.invariant_ref) {
1629
+ const invariantExists = world.invariants.some((i) => i.id === guard.invariant_ref);
1630
+ if (!invariantExists) {
1631
+ findings.push(finding(
1632
+ `broken-guard-invariant-ref-${guard.id}`,
1633
+ `Guard "${guard.id}" references invariant "${guard.invariant_ref}" which does not exist`,
1634
+ "error",
1635
+ "referential-integrity",
1636
+ ["guards.json", "invariants.json"],
1637
+ guard.id
1638
+ ));
1639
+ }
1640
+ }
1641
+ }
1642
+ if (world.guards) {
1643
+ const vocabKeys = new Set(Object.keys(world.guards.intent_vocabulary));
1644
+ for (const guard of guards) {
1645
+ for (const patternKey of guard.intent_patterns) {
1646
+ if (!vocabKeys.has(patternKey)) {
1647
+ findings.push(finding(
1648
+ `broken-guard-pattern-${guard.id}-${patternKey}`,
1649
+ `Guard "${guard.id}" references intent pattern "${patternKey}" which is not in intent_vocabulary`,
1650
+ "error",
1651
+ "referential-integrity",
1652
+ ["guards.json"],
1653
+ guard.id
1654
+ ));
1655
+ }
1656
+ }
1657
+ }
1658
+ }
1659
+ }
1660
+ function checkContradictions(world, findings) {
1661
+ if (!world.rules || world.rules.length < 2) return;
1662
+ checkCircularExclusiveWith(world.rules, findings);
1663
+ const tensions = detectSemanticTensions(world.rules);
1664
+ for (let i = 0; i < tensions.length; i++) {
1665
+ const tension = tensions[i];
1666
+ findings.push(finding(
1667
+ `semantic-tension-${i}`,
1668
+ tension.message,
1669
+ tension.severity,
1670
+ "semantic-tension",
1671
+ tension.affectedBlocks
1672
+ ));
1673
+ }
1674
+ }
1675
+ function checkCircularExclusiveWith(rules, findings) {
1676
+ const exclusiveMap = /* @__PURE__ */ new Map();
1677
+ for (const rule of rules) {
1678
+ if (rule.exclusive_with) {
1679
+ exclusiveMap.set(rule.id, rule.exclusive_with);
1680
+ }
1681
+ }
1682
+ const reportedPairs = /* @__PURE__ */ new Set();
1683
+ for (const [ruleA, ruleB] of exclusiveMap) {
1684
+ if (exclusiveMap.get(ruleB) === ruleA) {
1685
+ const pairKey = [ruleA, ruleB].sort().join("::");
1686
+ if (!reportedPairs.has(pairKey)) {
1687
+ reportedPairs.add(pairKey);
1688
+ findings.push(finding(
1689
+ `mutual-exclusion-${pairKey.replace("::", "-")}`,
1690
+ `Rules "${ruleA}" and "${ruleB}" are mutually exclusive \u2014 only one can fire per evaluation`,
1691
+ "info",
1692
+ "semantic-tension",
1693
+ ["rules/"],
1694
+ `${ruleA}, ${ruleB}`
1695
+ ));
1696
+ }
1697
+ }
1698
+ }
1699
+ const reportedChains = /* @__PURE__ */ new Set();
1700
+ for (const startRule of exclusiveMap.keys()) {
1701
+ let current = startRule;
1702
+ const visited = /* @__PURE__ */ new Set();
1703
+ const chain = [];
1704
+ while (current && !visited.has(current)) {
1705
+ visited.add(current);
1706
+ chain.push(current);
1707
+ current = exclusiveMap.get(current);
1708
+ }
1709
+ if (current === startRule && visited.size > 2) {
1710
+ const chainKey = [...chain].sort().join("::");
1711
+ if (!reportedChains.has(chainKey)) {
1712
+ reportedChains.add(chainKey);
1713
+ findings.push(finding(
1714
+ `circular-exclusive-chain-${startRule}`,
1715
+ `Circular exclusive_with chain: ${chain.join(" \u2192 ")} \u2192 ${startRule} (${chain.length} rules)`,
1716
+ "warning",
1717
+ "contradiction",
1718
+ ["rules/"],
1719
+ chain.join(", ")
1720
+ ));
1721
+ }
1722
+ }
1723
+ }
1724
+ }
1725
+ function detectSemanticTensions(rules) {
1726
+ const results = [];
1727
+ if (rules.length < 2) return results;
1728
+ const exclusivePairs = /* @__PURE__ */ new Set();
1729
+ for (const rule of rules) {
1730
+ if (rule.exclusive_with) {
1731
+ exclusivePairs.add(`${rule.id}:${rule.exclusive_with}`);
1732
+ exclusivePairs.add(`${rule.exclusive_with}:${rule.id}`);
1733
+ }
1734
+ }
1735
+ const isExclusive = (a, b) => exclusivePairs.has(`${a}:${b}`);
1736
+ const targetEffects = /* @__PURE__ */ new Map();
1737
+ for (const rule of rules) {
1738
+ const allEffects = [
1739
+ ...rule.effects || [],
1740
+ ...(rule.effects_conditional || []).flatMap((c2) => c2.effects)
1741
+ ];
1742
+ for (const effect of allEffects) {
1743
+ const direction = classifyEffectDirection(effect);
1744
+ if (direction === "neutral") continue;
1745
+ const entries = targetEffects.get(effect.target) ?? [];
1746
+ entries.push({ ruleId: rule.id, ruleLabel: rule.label, direction, effect });
1747
+ targetEffects.set(effect.target, entries);
1748
+ }
1749
+ }
1750
+ for (const [target, effects] of targetEffects) {
1751
+ const increasing = effects.filter((e) => e.direction === "increase");
1752
+ const decreasing = effects.filter((e) => e.direction === "decrease");
1753
+ if (increasing.length === 0 || decreasing.length === 0) continue;
1754
+ const incRuleIds = /* @__PURE__ */ new Set();
1755
+ const decRuleIds = /* @__PURE__ */ new Set();
1756
+ for (const inc of increasing) {
1757
+ for (const dec of decreasing) {
1758
+ if (inc.ruleId === dec.ruleId) continue;
1759
+ if (isExclusive(inc.ruleId, dec.ruleId)) continue;
1760
+ incRuleIds.add(inc.ruleId);
1761
+ decRuleIds.add(dec.ruleId);
1762
+ }
1763
+ }
1764
+ if (incRuleIds.size === 0 || decRuleIds.size === 0) continue;
1765
+ const incLabels = [...incRuleIds].map((id) => {
1766
+ const e = increasing.find((e2) => e2.ruleId === id);
1767
+ return `"${e.ruleLabel}" (${describeEffect(e.effect)})`;
1768
+ });
1769
+ const decLabels = [...decRuleIds].map((id) => {
1770
+ const e = decreasing.find((e2) => e2.ruleId === id);
1771
+ return `"${e.ruleLabel}" (${describeEffect(e.effect)})`;
1772
+ });
1773
+ results.push({
1774
+ type: "semantic_tension",
1775
+ severity: "warning",
1776
+ message: `Opposing effects on "${target}": ${incLabels.join(", ")} increase it while ${decLabels.join(", ")} decrease it`,
1777
+ affectedBlocks: ["rules"]
1778
+ });
1779
+ }
1780
+ const setEffects = /* @__PURE__ */ new Map();
1781
+ for (const rule of rules) {
1782
+ const allEffects = [
1783
+ ...rule.effects || [],
1784
+ ...(rule.effects_conditional || []).flatMap((c2) => c2.effects)
1785
+ ];
1786
+ for (const effect of allEffects) {
1787
+ if (effect.operation !== "set" && effect.operation !== "set_boolean" && effect.operation !== "set_dynamic") continue;
1788
+ const entries = setEffects.get(effect.target) ?? [];
1789
+ entries.push({ ruleId: rule.id, ruleLabel: rule.label, value: effect.value });
1790
+ setEffects.set(effect.target, entries);
1791
+ }
1792
+ }
1793
+ for (const [target, effects] of setEffects) {
1794
+ if (effects.length < 2) continue;
1795
+ const byValue = /* @__PURE__ */ new Map();
1796
+ for (const e of effects) {
1797
+ const key = String(e.value);
1798
+ const existing = byValue.get(key) ?? [];
1799
+ existing.push(e);
1800
+ byValue.set(key, existing);
1801
+ }
1802
+ if (byValue.size < 2) continue;
1803
+ const valueGroups = [...byValue.entries()];
1804
+ const involvedRules = [];
1805
+ for (let i = 0; i < valueGroups.length; i++) {
1806
+ for (let j = i + 1; j < valueGroups.length; j++) {
1807
+ for (const a of valueGroups[i][1]) {
1808
+ for (const b of valueGroups[j][1]) {
1809
+ if (a.ruleId === b.ruleId) continue;
1810
+ if (isExclusive(a.ruleId, b.ruleId)) continue;
1811
+ if (!involvedRules.includes(a.ruleId)) involvedRules.push(a.ruleId);
1812
+ if (!involvedRules.includes(b.ruleId)) involvedRules.push(b.ruleId);
1813
+ }
1814
+ }
1815
+ }
1816
+ }
1817
+ if (involvedRules.length === 0) continue;
1818
+ const descriptions = involvedRules.map((id) => {
1819
+ const e = effects.find((e2) => e2.ruleId === id);
1820
+ return `"${e.ruleLabel}" sets to ${e.value}`;
1821
+ });
1822
+ results.push({
1823
+ type: "semantic_tension",
1824
+ severity: "warning",
1825
+ message: `Conflicting set operations on "${target}": ${descriptions.join(", ")}`,
1826
+ affectedBlocks: ["rules"]
1827
+ });
1828
+ }
1829
+ return results;
1830
+ }
1831
+ function classifyEffectDirection(effect) {
1832
+ if (effect.operation === "multiply" || effect.operation === "multiply_dynamic") {
1833
+ const val = typeof effect.value === "number" ? effect.value : 1;
1834
+ return val > 1 ? "increase" : val < 1 ? "decrease" : "neutral";
1835
+ }
1836
+ if (effect.operation === "add" || effect.operation === "add_dynamic") {
1837
+ const val = typeof effect.value === "number" ? effect.value : 0;
1838
+ return val > 0 ? "increase" : val < 0 ? "decrease" : "neutral";
1839
+ }
1840
+ if (effect.operation === "subtract" || effect.operation === "subtract_dynamic") {
1841
+ const val = typeof effect.value === "number" ? effect.value : 0;
1842
+ return val > 0 ? "decrease" : val < 0 ? "increase" : "neutral";
1843
+ }
1844
+ return "neutral";
1845
+ }
1846
+ function describeEffect(effect) {
1847
+ switch (effect.operation) {
1848
+ case "multiply":
1849
+ case "multiply_dynamic":
1850
+ return `multiplies by ${effect.value}`;
1851
+ case "add":
1852
+ case "add_dynamic":
1853
+ return `adds ${effect.value}`;
1854
+ case "subtract":
1855
+ case "subtract_dynamic":
1856
+ return `subtracts ${effect.value}`;
1857
+ case "set":
1858
+ case "set_dynamic":
1859
+ case "set_boolean":
1860
+ return `sets to ${effect.value}`;
1861
+ default:
1862
+ return `${effect.operation} ${effect.value}`;
1863
+ }
1864
+ }
1865
+ function checkOrphans(world, findings) {
1866
+ if (!world.stateSchema?.variables || !world.rules) return;
1867
+ const referencedVars = /* @__PURE__ */ new Set();
1868
+ for (const rule of world.rules) {
1869
+ for (const trigger of rule.triggers) {
1870
+ referencedVars.add(trigger.field);
1871
+ }
1872
+ for (const effect of rule.effects ?? []) {
1873
+ referencedVars.add(effect.target);
1874
+ }
1875
+ if (rule.collapse_check) {
1876
+ referencedVars.add(rule.collapse_check.field);
1877
+ }
1878
+ }
1879
+ for (const gate of world.gates?.viability_classification ?? []) {
1880
+ referencedVars.add(gate.field);
1881
+ }
1882
+ for (const varId of Object.keys(world.stateSchema.variables)) {
1883
+ if (!referencedVars.has(varId)) {
1884
+ findings.push(finding(
1885
+ `orphan-variable-${varId}`,
1886
+ `State variable "${varId}" is declared but never referenced by any rule or gate`,
1887
+ "warning",
1888
+ "orphan",
1889
+ ["state-schema.json"],
1890
+ varId,
1891
+ "Remove this variable or add rules that reference it"
1892
+ ));
1893
+ }
1894
+ }
1895
+ const effectTargets = /* @__PURE__ */ new Set();
1896
+ for (const rule of world.rules) {
1897
+ for (const effect of rule.effects ?? []) {
1898
+ effectTargets.add(effect.target);
1899
+ }
1900
+ }
1901
+ for (const outcome of world.outcomes?.computed_outcomes ?? []) {
1902
+ if (outcome.assignment === "external") continue;
1903
+ if (!effectTargets.has(outcome.id) && !outcome.derived_from) {
1904
+ findings.push(finding(
1905
+ `orphan-outcome-${outcome.id}`,
1906
+ `Outcome "${outcome.id}" is declared but no rule produces it`,
1907
+ "warning",
1908
+ "orphan",
1909
+ ["outcomes.json", "rules/"],
1910
+ outcome.id
1911
+ ));
1912
+ }
1913
+ }
1914
+ }
1915
+ function checkSchemaViolations(world, findings) {
1916
+ if (!world.stateSchema?.variables) return;
1917
+ for (const [varId, variable] of Object.entries(world.stateSchema.variables)) {
1918
+ if (variable.type === "number") {
1919
+ const def = variable.default;
1920
+ if (variable.min !== void 0 && def < variable.min) {
1921
+ findings.push(finding(
1922
+ `default-below-min-${varId}`,
1923
+ `Variable "${varId}" default (${def}) is below declared min (${variable.min})`,
1924
+ "error",
1925
+ "schema-violation",
1926
+ ["state-schema.json"],
1927
+ varId
1928
+ ));
1929
+ }
1930
+ if (variable.max !== void 0 && def > variable.max) {
1931
+ findings.push(finding(
1932
+ `default-above-max-${varId}`,
1933
+ `Variable "${varId}" default (${def}) is above declared max (${variable.max})`,
1934
+ "error",
1935
+ "schema-violation",
1936
+ ["state-schema.json"],
1937
+ varId
1938
+ ));
1939
+ }
1940
+ }
1941
+ if (variable.type === "enum") {
1942
+ if (!variable.options || variable.options.length === 0) {
1943
+ findings.push(finding(
1944
+ `enum-no-options-${varId}`,
1945
+ `Enum variable "${varId}" has no options declared`,
1946
+ "error",
1947
+ "schema-violation",
1948
+ ["state-schema.json"],
1949
+ varId
1950
+ ));
1951
+ } else if (!variable.options.includes(variable.default)) {
1952
+ findings.push(finding(
1953
+ `enum-default-invalid-${varId}`,
1954
+ `Enum variable "${varId}" default "${variable.default}" is not in declared options`,
1955
+ "error",
1956
+ "schema-violation",
1957
+ ["state-schema.json"],
1958
+ varId
1959
+ ));
1960
+ }
1961
+ }
1962
+ }
1963
+ for (const [presetName, preset] of Object.entries(world.stateSchema.presets ?? {})) {
1964
+ for (const [varId, value] of Object.entries(preset.values)) {
1965
+ const variable = world.stateSchema.variables[varId];
1966
+ if (!variable) {
1967
+ findings.push(finding(
1968
+ `preset-undeclared-var-${presetName}-${varId}`,
1969
+ `Preset "${presetName}" sets undeclared variable "${varId}"`,
1970
+ "error",
1971
+ "referential-integrity",
1972
+ ["state-schema.json"],
1973
+ presetName
1974
+ ));
1975
+ continue;
1976
+ }
1977
+ if (variable.type === "number" && typeof value === "number") {
1978
+ if (variable.min !== void 0 && value < variable.min) {
1979
+ findings.push(finding(
1980
+ `preset-below-min-${presetName}-${varId}`,
1981
+ `Preset "${presetName}" sets "${varId}" to ${value}, below min ${variable.min}`,
1982
+ "warning",
1983
+ "schema-violation",
1984
+ ["state-schema.json"],
1985
+ presetName
1986
+ ));
1987
+ }
1988
+ if (variable.max !== void 0 && value > variable.max) {
1989
+ findings.push(finding(
1990
+ `preset-above-max-${presetName}-${varId}`,
1991
+ `Preset "${presetName}" sets "${varId}" to ${value}, above max ${variable.max}`,
1992
+ "warning",
1993
+ "schema-violation",
1994
+ ["state-schema.json"],
1995
+ presetName
1996
+ ));
1997
+ }
1998
+ }
1999
+ }
2000
+ }
2001
+ }
2002
+ function computeCompletenessScore(world) {
2003
+ let score = 0;
2004
+ const total = 9;
2005
+ if (world.world?.world_id && world.world?.name && world.world?.thesis) score++;
2006
+ if (world.invariants && world.invariants.length > 0) score++;
2007
+ if (world.assumptions?.profiles && Object.keys(world.assumptions.profiles).length > 0) score++;
2008
+ if (world.stateSchema?.variables && Object.keys(world.stateSchema.variables).length > 0) score++;
2009
+ if (world.rules && world.rules.length > 0) score++;
2010
+ if (world.gates?.viability_classification && world.gates.viability_classification.length > 0) score++;
2011
+ if (world.outcomes?.computed_outcomes && world.outcomes.computed_outcomes.length > 0) score++;
2012
+ if (world.guards?.guards && world.guards.guards.length > 0) score++;
2013
+ if (world.metadata) score++;
2014
+ return Math.round(score / total * 100);
2015
+ }
2016
+ function computeInvariantCoverage(world) {
2017
+ if (!world.invariants || world.invariants.length === 0) return 0;
2018
+ const guards = world.guards?.guards ?? [];
2019
+ let covered = 0;
2020
+ for (const invariant of world.invariants) {
2021
+ const hasGuard = guards.some((g) => g.invariant_ref === invariant.id && g.immutable);
2022
+ if (hasGuard) covered++;
2023
+ }
2024
+ return Math.round(covered / world.invariants.length * 100);
2025
+ }
2026
+ function finding(id, message, severity, category, affectedBlocks, source, suggestion) {
2027
+ const f = { id, message, severity, category, affectedBlocks };
2028
+ if (source) f.source = source;
2029
+ if (suggestion) f.suggestion = suggestion;
2030
+ return f;
2031
+ }
2032
+
2033
+ // src/contracts/validate-contract.ts
2034
+ var VALIDATE_EXIT_CODES = {
2035
+ PASS: 0,
2036
+ FAIL: 1,
2037
+ WARN: 2,
2038
+ ERROR: 3
2039
+ };
2040
+
2041
+ // src/engine/bootstrap-parser.ts
2042
+ function splitSections(markdown) {
2043
+ const lines = markdown.split("\n");
2044
+ let frontmatter = "";
2045
+ let bodyStart = 0;
2046
+ if (lines[0]?.trim() === "---") {
2047
+ const endIdx = lines.indexOf("---", 1);
2048
+ if (endIdx > 0) {
2049
+ frontmatter = lines.slice(1, endIdx).join("\n");
2050
+ bodyStart = endIdx + 1;
2051
+ }
2052
+ }
2053
+ const sections = [];
2054
+ let currentSection = null;
2055
+ const contentLines = [];
2056
+ for (let i = bodyStart; i < lines.length; i++) {
2057
+ const line = lines[i];
2058
+ if (line.startsWith("# ")) {
2059
+ if (currentSection) {
2060
+ currentSection.content = contentLines.join("\n").trim();
2061
+ sections.push(currentSection);
2062
+ contentLines.length = 0;
2063
+ }
2064
+ currentSection = {
2065
+ name: line.replace(/^#\s+/, "").trim(),
2066
+ content: "",
2067
+ startLine: i + 1
2068
+ // 1-based
2069
+ };
2070
+ } else if (currentSection) {
2071
+ contentLines.push(line);
2072
+ }
2073
+ }
2074
+ if (currentSection) {
2075
+ currentSection.content = contentLines.join("\n").trim();
2076
+ sections.push(currentSection);
2077
+ }
2078
+ return { frontmatter, sections };
2079
+ }
2080
+ function parseFrontmatter(yaml, issues) {
2081
+ const result = {};
2082
+ for (const line of yaml.split("\n")) {
2083
+ const trimmed = line.trim();
2084
+ if (!trimmed || trimmed.startsWith("#")) continue;
2085
+ const colonIdx = trimmed.indexOf(":");
2086
+ if (colonIdx === -1) continue;
2087
+ const key = trimmed.slice(0, colonIdx).trim();
2088
+ const value = trimmed.slice(colonIdx + 1).trim();
2089
+ result[key] = value;
2090
+ }
2091
+ if (!result.world_id) {
2092
+ issues.push({ line: 1, section: "frontmatter", message: "Missing world_id in frontmatter", severity: "error" });
2093
+ }
2094
+ if (!result.name) {
2095
+ issues.push({ line: 1, section: "frontmatter", message: "Missing name in frontmatter", severity: "error" });
2096
+ }
2097
+ return {
2098
+ world_id: result.world_id ?? "",
2099
+ name: result.name ?? "",
2100
+ version: result.version,
2101
+ runtime_mode: result.runtime_mode,
2102
+ default_profile: result.default_profile,
2103
+ alternative_profile: result.alternative_profile
2104
+ };
2105
+ }
2106
+ function parseThesis(content, startLine, issues) {
2107
+ const thesis = content.trim();
2108
+ if (!thesis) {
2109
+ issues.push({ line: startLine, section: "Thesis", message: "Thesis section is empty", severity: "error" });
2110
+ }
2111
+ return thesis;
2112
+ }
2113
+ function parseInvariants(content, startLine, issues) {
2114
+ const invariants = [];
2115
+ const lines = content.split("\n");
2116
+ for (let i = 0; i < lines.length; i++) {
2117
+ const line = lines[i].trim();
2118
+ if (!line.startsWith("- ")) continue;
2119
+ const lineNum = startLine + i + 1;
2120
+ const match = line.match(
2121
+ /^-\s+`([^`]+)`\s*[—–-]\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
2122
+ );
2123
+ if (match) {
2124
+ const id = match[1];
2125
+ const label = match[2].trim();
2126
+ const parens = match[3] ?? "structural, immutable";
2127
+ const enforcement = parens.includes("prompt") ? "prompt" : parens.includes("operational") ? "operational" : "structural";
2128
+ const mutable = parens.includes("mutable") && !parens.includes("immutable");
2129
+ invariants.push({ id, label, enforcement, mutable, line: lineNum });
2130
+ } else {
2131
+ const fallback = line.match(/^-\s+\*\*([^*]+)\*\*\s*[—–-]\s*(.+)/);
2132
+ if (fallback) {
2133
+ const id = fallback[1].toLowerCase().replace(/\s+/g, "_");
2134
+ const label = fallback[2].trim();
2135
+ invariants.push({ id, label, enforcement: "structural", mutable: false, line: lineNum });
2136
+ } else {
2137
+ issues.push({ line: lineNum, section: "Invariants", message: `Could not parse invariant: "${line}"`, severity: "warning" });
2138
+ }
2139
+ }
2140
+ }
2141
+ if (invariants.length === 0) {
2142
+ issues.push({ line: startLine, section: "Invariants", message: "No invariants found", severity: "error" });
2143
+ }
2144
+ return invariants;
2145
+ }
2146
+ function parseStateVariables(content, startLine, issues) {
2147
+ const variables = [];
2148
+ const subSections = splitH2Sections(content, startLine);
2149
+ for (const sub of subSections) {
2150
+ const props = parseKeyValueBullets(sub.content);
2151
+ const lineNum = sub.startLine;
2152
+ const type = props.type;
2153
+ if (!type) {
2154
+ issues.push({ line: lineNum, section: "State", message: `Variable "${sub.name}" missing type`, severity: "error" });
2155
+ continue;
2156
+ }
2157
+ let defaultVal = props.default ?? "";
2158
+ if (type === "number") {
2159
+ defaultVal = parseFloat(String(defaultVal)) || 0;
2160
+ } else if (type === "boolean") {
2161
+ defaultVal = String(defaultVal).toLowerCase() === "true";
2162
+ }
2163
+ const variable = {
2164
+ id: sub.name,
2165
+ type,
2166
+ default: defaultVal,
2167
+ label: props.label ?? sub.name,
2168
+ description: props.description ?? "",
2169
+ line: lineNum
2170
+ };
2171
+ if (type === "number") {
2172
+ if (props.min !== void 0) variable.min = parseFloat(props.min);
2173
+ if (props.max !== void 0) variable.max = parseFloat(props.max);
2174
+ if (props.step !== void 0) variable.step = parseFloat(props.step);
2175
+ }
2176
+ if (type === "enum" && props.options) {
2177
+ variable.options = props.options.split(",").map((s) => s.trim());
2178
+ }
2179
+ variables.push(variable);
2180
+ }
2181
+ return variables;
2182
+ }
2183
+ function parseAssumptions(content, startLine, issues) {
2184
+ const profiles = [];
2185
+ const subSections = splitH2Sections(content, startLine);
2186
+ for (const sub of subSections) {
2187
+ const props = parseKeyValueBullets(sub.content);
2188
+ const name = props.name ?? sub.name;
2189
+ const description = props.description ?? "";
2190
+ const parameters = {};
2191
+ for (const [key, val] of Object.entries(props)) {
2192
+ if (key === "name" || key === "description") continue;
2193
+ if (val === "true") parameters[key] = true;
2194
+ else if (val === "false") parameters[key] = false;
2195
+ else if (!isNaN(Number(val)) && val.trim() !== "") parameters[key] = Number(val);
2196
+ else parameters[key] = val;
2197
+ }
2198
+ profiles.push({
2199
+ id: sub.name,
2200
+ name,
2201
+ description,
2202
+ parameters,
2203
+ line: sub.startLine
2204
+ });
2205
+ }
2206
+ return profiles;
2207
+ }
2208
+ function parseRules(content, startLine, issues) {
2209
+ const rules = [];
2210
+ const subSections = splitH2Sections(content, startLine);
2211
+ for (let ruleIdx = 0; ruleIdx < subSections.length; ruleIdx++) {
2212
+ const sub = subSections[ruleIdx];
2213
+ const lineNum = sub.startLine;
2214
+ const headingMatch = sub.name.match(/^([^:]+):\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/);
2215
+ const id = headingMatch ? headingMatch[1].trim() : `rule-${String(ruleIdx + 1).padStart(3, "0")}`;
2216
+ const label = headingMatch ? headingMatch[2].trim() : sub.name;
2217
+ const severity = headingMatch?.[3]?.trim() ?? "degradation";
2218
+ const lines = sub.content.split("\n");
2219
+ let description = "";
2220
+ const triggers = [];
2221
+ const effects = [];
2222
+ let collapseCheck;
2223
+ const causalParts = {};
2224
+ for (let i = 0; i < lines.length; i++) {
2225
+ const line = lines[i].trim();
2226
+ if (line.startsWith("When ")) {
2227
+ const triggerStr = line.slice(5);
2228
+ const parts = triggerStr.split(/\s+AND\s+/i);
2229
+ for (const part of parts) {
2230
+ const trigger = parseTriggerExpression(part.trim());
2231
+ if (trigger) triggers.push(trigger);
2232
+ else issues.push({ line: lineNum + i, section: "Rules", message: `Could not parse trigger: "${part}"`, severity: "warning" });
2233
+ }
2234
+ } else if (line.startsWith("Then ")) {
2235
+ const effectStr = line.slice(5);
2236
+ const parts = effectStr.split(",");
2237
+ for (const part of parts) {
2238
+ const effect = parseEffectExpression(part.trim());
2239
+ if (effect) effects.push(effect);
2240
+ else issues.push({ line: lineNum + i, section: "Rules", message: `Could not parse effect: "${part}"`, severity: "warning" });
2241
+ }
2242
+ } else if (line.startsWith("Collapse:")) {
2243
+ const collapseStr = line.slice(9).trim();
2244
+ const collapse = parseCollapseExpression(collapseStr);
2245
+ if (collapse) collapseCheck = collapse;
2246
+ else issues.push({ line: lineNum + i, section: "Rules", message: `Could not parse collapse: "${collapseStr}"`, severity: "warning" });
2247
+ } else if (line.startsWith("> ")) {
2248
+ const causalMatch = line.match(/^>\s*(trigger|rule|shift|effect):\s*(.+)/i);
2249
+ if (causalMatch) {
2250
+ causalParts[causalMatch[1].toLowerCase()] = causalMatch[2].trim();
2251
+ }
2252
+ } else if (line && !line.startsWith("-") && !line.startsWith("#")) {
2253
+ if (!description) description = line;
2254
+ else description += " " + line;
2255
+ }
2256
+ }
2257
+ if (triggers.length === 0) {
2258
+ issues.push({ line: lineNum, section: "Rules", message: `Rule "${id}" has no triggers (missing "When" line)`, severity: "warning" });
2259
+ }
2260
+ if (effects.length === 0) {
2261
+ issues.push({ line: lineNum, section: "Rules", message: `Rule "${id}" has no effects (missing "Then" line)`, severity: "warning" });
2262
+ }
2263
+ const causal_translation = Object.keys(causalParts).length > 0 ? {
2264
+ trigger_text: causalParts.trigger ?? "",
2265
+ rule_text: causalParts.rule ?? "",
2266
+ shift_text: causalParts.shift ?? "",
2267
+ effect_text: causalParts.effect ?? ""
2268
+ } : void 0;
2269
+ rules.push({
2270
+ id,
2271
+ label,
2272
+ severity,
2273
+ description: description || void 0,
2274
+ order: ruleIdx + 1,
2275
+ triggers,
2276
+ effects,
2277
+ collapse_check: collapseCheck,
2278
+ causal_translation,
2279
+ line: lineNum
2280
+ });
2281
+ }
2282
+ return rules;
2283
+ }
2284
+ function parseTriggerExpression(expr) {
2285
+ const match = expr.match(
2286
+ /^(\w+)\s*(==|!=|>=|<=|>|<|in)\s*(.+?)\s*\[(state|assumption)\]\s*$/
2287
+ );
2288
+ if (!match) return null;
2289
+ const field = match[1];
2290
+ const operator = match[2];
2291
+ let value = match[3].trim();
2292
+ const source = match[4];
2293
+ value = parseValueLiteral(value);
2294
+ return { field, operator, value, source };
2295
+ }
2296
+ function parseEffectExpression(expr) {
2297
+ const compound = expr.match(/^(\w+)\s*(\*=|\+=|-=)\s*(.+)$/);
2298
+ if (compound) {
2299
+ const target = compound[1];
2300
+ const op = compound[2];
2301
+ const value = parseValueLiteral(compound[3].trim());
2302
+ const operationMap = {
2303
+ "*=": "multiply",
2304
+ "+=": "add",
2305
+ "-=": "subtract"
2306
+ };
2307
+ return { target, operation: operationMap[op], value };
2308
+ }
2309
+ const assignment = expr.match(/^(\w+)\s*=\s*(.+)$/);
2310
+ if (assignment) {
2311
+ const target = assignment[1];
2312
+ const value = parseValueLiteral(assignment[2].trim());
2313
+ const operation = typeof value === "boolean" ? "set_boolean" : "set";
2314
+ return { target, operation, value };
2315
+ }
2316
+ return null;
2317
+ }
2318
+ function parseCollapseExpression(expr) {
2319
+ const cleaned = expr.replace(/\s*→.*$/, "").trim();
2320
+ const match = cleaned.match(/^(\w+)\s*(==|!=|>=|<=|>|<)\s*([\d.]+)$/);
2321
+ if (!match) return null;
2322
+ return {
2323
+ field: match[1],
2324
+ operator: match[2],
2325
+ value: parseFloat(match[3])
2326
+ };
2327
+ }
2328
+ function parseGates(content, startLine, issues) {
2329
+ const gates = [];
2330
+ const lines = content.split("\n");
2331
+ for (let i = 0; i < lines.length; i++) {
2332
+ const line = lines[i].trim();
2333
+ if (!line.startsWith("- ")) continue;
2334
+ const lineNum = startLine + i + 1;
2335
+ const match = line.match(/^-\s+(\w+):\s*(\w+)\s*(==|!=|>=|<=|>|<)\s*([\d.]+)/);
2336
+ if (match) {
2337
+ gates.push({
2338
+ status: match[1],
2339
+ field: match[2],
2340
+ operator: match[3],
2341
+ value: parseFloat(match[4]),
2342
+ line: lineNum
2343
+ });
2344
+ } else {
2345
+ issues.push({ line: lineNum, section: "Gates", message: `Could not parse gate: "${line}"`, severity: "warning" });
2346
+ }
2347
+ }
2348
+ if (gates.length === 0) {
2349
+ issues.push({ line: startLine, section: "Gates", message: "No gates found", severity: "error" });
2350
+ }
2351
+ return gates;
2352
+ }
2353
+ function parseOutcomes(content, startLine, issues) {
2354
+ const outcomes = [];
2355
+ const subSections = splitH2Sections(content, startLine);
2356
+ for (const sub of subSections) {
2357
+ const props = parseKeyValueBullets(sub.content);
2358
+ const outcome = {
2359
+ id: sub.name,
2360
+ type: props.type ?? "number",
2361
+ label: props.label ?? sub.name,
2362
+ line: sub.startLine
2363
+ };
2364
+ if (props.range) {
2365
+ const rangeParts = props.range.split("-").map(Number);
2366
+ if (rangeParts.length === 2 && !isNaN(rangeParts[0]) && !isNaN(rangeParts[1])) {
2367
+ outcome.range = [rangeParts[0], rangeParts[1]];
2368
+ }
2369
+ }
2370
+ if (props.display) outcome.display = props.display;
2371
+ if (props.primary) outcome.primary = props.primary === "true";
2372
+ if (props.assignment) outcome.assignment = props.assignment;
2373
+ outcomes.push(outcome);
2374
+ }
2375
+ return outcomes;
2376
+ }
2377
+ function splitH2Sections(content, baseStartLine) {
2378
+ const lines = content.split("\n");
2379
+ const sections = [];
2380
+ let current = null;
2381
+ const contentLines = [];
2382
+ for (let i = 0; i < lines.length; i++) {
2383
+ const line = lines[i];
2384
+ if (line.startsWith("## ")) {
2385
+ if (current) {
2386
+ current.content = contentLines.join("\n").trim();
2387
+ sections.push(current);
2388
+ contentLines.length = 0;
2389
+ }
2390
+ current = {
2391
+ name: line.replace(/^##\s+/, "").trim(),
2392
+ content: "",
2393
+ startLine: baseStartLine + i + 1
2394
+ };
2395
+ } else if (current) {
2396
+ contentLines.push(line);
2397
+ }
2398
+ }
2399
+ if (current) {
2400
+ current.content = contentLines.join("\n").trim();
2401
+ sections.push(current);
2402
+ }
2403
+ return sections;
2404
+ }
2405
+ function parseKeyValueBullets(content) {
2406
+ const result = {};
2407
+ for (const line of content.split("\n")) {
2408
+ const trimmed = line.trim();
2409
+ const match = trimmed.match(/^-\s+(\w[\w\s]*?):\s*(.+)$/);
2410
+ if (match) {
2411
+ result[match[1].trim().toLowerCase().replace(/\s+/g, "_")] = match[2].trim();
2412
+ }
2413
+ }
2414
+ return result;
2415
+ }
2416
+ function parseValueLiteral(raw) {
2417
+ if (raw === "true") return true;
2418
+ if (raw === "false") return false;
2419
+ if (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("'") && raw.endsWith("'")) {
2420
+ return raw.slice(1, -1);
2421
+ }
2422
+ const num = Number(raw);
2423
+ if (!isNaN(num) && raw.trim() !== "") return num;
2424
+ return raw;
2425
+ }
2426
+ function parseWorldMarkdown(markdown) {
2427
+ const issues = [];
2428
+ const { frontmatter: fmRaw, sections } = splitSections(markdown);
2429
+ const frontmatter = parseFrontmatter(fmRaw, issues);
2430
+ const findSection = (name) => sections.find((s) => s.name.toLowerCase() === name.toLowerCase());
2431
+ const thesisSection = findSection("Thesis");
2432
+ const thesis = thesisSection ? parseThesis(thesisSection.content, thesisSection.startLine, issues) : "";
2433
+ if (!thesisSection) {
2434
+ issues.push({ line: 0, section: "Thesis", message: "Missing # Thesis section", severity: "error" });
2435
+ }
2436
+ const invariantsSection = findSection("Invariants");
2437
+ const invariants = invariantsSection ? parseInvariants(invariantsSection.content, invariantsSection.startLine, issues) : [];
2438
+ if (!invariantsSection) {
2439
+ issues.push({ line: 0, section: "Invariants", message: "Missing # Invariants section", severity: "error" });
2440
+ }
2441
+ const stateSection = findSection("State");
2442
+ const stateVariables = stateSection ? parseStateVariables(stateSection.content, stateSection.startLine, issues) : [];
2443
+ if (!stateSection) {
2444
+ issues.push({ line: 0, section: "State", message: "Missing # State section", severity: "warning" });
2445
+ }
2446
+ const assumptionsSection = findSection("Assumptions");
2447
+ const assumptions = assumptionsSection ? parseAssumptions(assumptionsSection.content, assumptionsSection.startLine, issues) : [];
2448
+ if (!assumptionsSection) {
2449
+ issues.push({ line: 0, section: "Assumptions", message: "Missing # Assumptions section", severity: "warning" });
2450
+ }
2451
+ const rulesSection = findSection("Rules");
2452
+ const rules = rulesSection ? parseRules(rulesSection.content, rulesSection.startLine, issues) : [];
2453
+ if (!rulesSection) {
2454
+ issues.push({ line: 0, section: "Rules", message: "Missing # Rules section", severity: "warning" });
2455
+ }
2456
+ const gatesSection = findSection("Gates");
2457
+ const gates = gatesSection ? parseGates(gatesSection.content, gatesSection.startLine, issues) : [];
2458
+ if (!gatesSection) {
2459
+ issues.push({ line: 0, section: "Gates", message: "Missing # Gates section", severity: "warning" });
2460
+ }
2461
+ const outcomesSection = findSection("Outcomes");
2462
+ const outcomes = outcomesSection ? parseOutcomes(outcomesSection.content, outcomesSection.startLine, issues) : [];
2463
+ const parsedSections = sections.map((s) => s.name);
2464
+ const knownSections = /* @__PURE__ */ new Set(["thesis", "invariants", "state", "assumptions", "rules", "gates", "outcomes"]);
2465
+ for (const section of sections) {
2466
+ if (!knownSections.has(section.name.toLowerCase())) {
2467
+ issues.push({
2468
+ line: section.startLine,
2469
+ section: section.name,
2470
+ message: `Unrecognized section "${section.name}" \u2014 will be ignored`,
2471
+ severity: "info"
2472
+ });
2473
+ }
2474
+ }
2475
+ const hasErrors = issues.some((i) => i.severity === "error");
2476
+ if (!frontmatter.world_id || !thesis) {
2477
+ if (hasErrors) {
2478
+ return { world: null, issues };
2479
+ }
2480
+ }
2481
+ return {
2482
+ world: {
2483
+ frontmatter,
2484
+ thesis,
2485
+ invariants,
2486
+ stateVariables,
2487
+ assumptions,
2488
+ rules,
2489
+ gates,
2490
+ outcomes
2491
+ },
2492
+ issues
2493
+ };
2494
+ }
2495
+
2496
+ // src/engine/bootstrap-emitter.ts
2497
+ var GATE_DEFAULTS = {
2498
+ THRIVING: { color: "#0f6b3a", icon: "\u2726" },
2499
+ STABLE: { color: "#1856b8", icon: "\u25CF" },
2500
+ COMPRESSED: { color: "#a16207", icon: "\u25B2" },
2501
+ CRITICAL: { color: "#b91c1c", icon: "\u26A0" },
2502
+ MODEL_COLLAPSES: { color: "#7f1d1d", icon: "\u2715" }
2503
+ };
2504
+ function emitWorldDefinition(parsed) {
2505
+ const issues = [];
2506
+ const fm = parsed.frontmatter;
2507
+ const defaultProfile = fm.default_profile ?? parsed.assumptions[0]?.id ?? "baseline";
2508
+ const altProfile = fm.alternative_profile ?? parsed.assumptions[1]?.id ?? "alternative";
2509
+ const world = {
2510
+ world_id: fm.world_id,
2511
+ name: fm.name,
2512
+ thesis: parsed.thesis,
2513
+ version: fm.version ?? "1.0.0",
2514
+ runtime_mode: fm.runtime_mode ?? "SIMULATION",
2515
+ default_assumption_profile: defaultProfile,
2516
+ default_alternative_profile: altProfile,
2517
+ modules: parsed.rules.map((r) => r.id),
2518
+ players: {
2519
+ thinking_space: true,
2520
+ experience_space: true,
2521
+ action_space: true
2522
+ }
2523
+ };
2524
+ const invariants = parsed.invariants.map((inv) => ({
2525
+ id: inv.id,
2526
+ label: inv.label,
2527
+ enforcement: inv.enforcement === "prompt" ? "prompt" : "structural",
2528
+ mutable: false
2529
+ }));
2530
+ const profiles = {};
2531
+ const parameterDefinitions = {};
2532
+ for (let i = 0; i < parsed.assumptions.length; i++) {
2533
+ const profile = parsed.assumptions[i];
2534
+ const params = {};
2535
+ for (const [key, val] of Object.entries(profile.parameters)) {
2536
+ params[key] = String(val);
2537
+ if (!parameterDefinitions[key]) {
2538
+ parameterDefinitions[key] = {
2539
+ type: typeof val === "boolean" ? "boolean" : typeof val === "number" ? "number" : "enum",
2540
+ label: key.replace(/_/g, " ").replace(/\b\w/g, (c2) => c2.toUpperCase()),
2541
+ description: `Parameter: ${key}`
2542
+ };
2543
+ }
2544
+ }
2545
+ profiles[profile.id] = {
2546
+ name: profile.name,
2547
+ description: profile.description,
2548
+ is_default_baseline: i === 0 || profile.id === defaultProfile,
2549
+ is_default_alternative: i === 1 || profile.id === altProfile,
2550
+ parameters: params
2551
+ };
2552
+ }
2553
+ const assumptions = { profiles, parameter_definitions: parameterDefinitions };
2554
+ const variables = {};
2555
+ for (const v of parsed.stateVariables) {
2556
+ const stateVar = {
2557
+ type: v.type,
2558
+ default: v.default,
2559
+ mutable: true,
2560
+ label: v.label,
2561
+ description: v.description
2562
+ };
2563
+ if (v.type === "number") {
2564
+ if (v.min !== void 0) stateVar.min = v.min;
2565
+ if (v.max !== void 0) stateVar.max = v.max;
2566
+ if (v.step !== void 0) stateVar.step = v.step;
2567
+ }
2568
+ if (v.type === "enum" && v.options) {
2569
+ stateVar.options = v.options;
2570
+ }
2571
+ variables[v.id] = stateVar;
2572
+ }
2573
+ const stateSchema = { variables, presets: {} };
2574
+ const rules = parsed.rules.map((r) => {
2575
+ const triggers = r.triggers.map((t) => ({
2576
+ field: t.field,
2577
+ operator: t.operator,
2578
+ value: t.value,
2579
+ source: t.source
2580
+ }));
2581
+ const effects = r.effects.map((e) => ({
2582
+ target: e.target,
2583
+ operation: e.operation,
2584
+ value: e.value
2585
+ }));
2586
+ let collapse_check;
2587
+ if (r.collapse_check) {
2588
+ collapse_check = {
2589
+ field: r.collapse_check.field,
2590
+ operator: r.collapse_check.operator,
2591
+ value: r.collapse_check.value,
2592
+ result: "MODEL_COLLAPSES"
2593
+ };
2594
+ }
2595
+ const causal_translation = r.causal_translation ?? {
2596
+ trigger_text: "",
2597
+ rule_text: "",
2598
+ shift_text: "",
2599
+ effect_text: ""
2600
+ };
2601
+ const rule = {
2602
+ id: r.id,
2603
+ severity: r.severity,
2604
+ label: r.label,
2605
+ description: r.description ?? r.label,
2606
+ order: r.order,
2607
+ triggers,
2608
+ effects: effects.length > 0 ? effects : void 0,
2609
+ collapse_check,
2610
+ causal_translation
2611
+ };
2612
+ return rule;
2613
+ });
2614
+ const viabilityClassification = parsed.gates.map((g) => {
2615
+ const defaults = GATE_DEFAULTS[g.status] ?? { color: "#5c5a52", icon: "\u25CF" };
2616
+ return {
2617
+ status: g.status,
2618
+ field: g.field,
2619
+ operator: g.operator,
2620
+ value: g.value,
2621
+ color: defaults.color,
2622
+ icon: defaults.icon
2623
+ };
2624
+ });
2625
+ const gates = {
2626
+ viability_classification: viabilityClassification,
2627
+ structural_override: {
2628
+ description: "Rules with severity=structural and triggered collapse_check force MODEL_COLLAPSES regardless of final margin.",
2629
+ enforcement: "mandatory"
2630
+ },
2631
+ sustainability_threshold: 0.1,
2632
+ collapse_visual: {
2633
+ background: "#1c1917",
2634
+ text: "#fef2f2",
2635
+ border: "#b91c1c",
2636
+ label: "Structural Failure"
2637
+ }
2638
+ };
2639
+ const computedOutcomes = parsed.outcomes.map((o) => {
2640
+ const outcome = {
2641
+ id: o.id,
2642
+ type: o.type,
2643
+ label: o.label,
2644
+ show_in_comparison: true
2645
+ };
2646
+ if (o.range) outcome.range = o.range;
2647
+ if (o.display) outcome.display_as = o.display;
2648
+ if (o.primary) outcome.primary = o.primary;
2649
+ if (o.assignment) outcome.assignment = o.assignment;
2650
+ return outcome;
2651
+ });
2652
+ const outcomes = {
2653
+ computed_outcomes: computedOutcomes,
2654
+ comparison_layout: {
2655
+ primary_card: computedOutcomes.find((o) => o.primary)?.id ?? computedOutcomes[0]?.id ?? "",
2656
+ status_badge: "viability_status",
2657
+ structural_indicators: rules.filter((r) => r.severity === "structural").map((r) => r.id)
2658
+ }
2659
+ };
2660
+ const metadata = {
2661
+ format_version: "1.0.0",
2662
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
2663
+ last_modified: (/* @__PURE__ */ new Date()).toISOString(),
2664
+ authoring_method: "manual-authoring"
2665
+ };
2666
+ const worldDefinition = {
2667
+ world,
2668
+ invariants,
2669
+ assumptions,
2670
+ stateSchema,
2671
+ rules,
2672
+ gates,
2673
+ outcomes,
2674
+ metadata
2675
+ };
2676
+ return { world: worldDefinition, issues };
2677
+ }
2678
+
2679
+ // src/contracts/bootstrap-contract.ts
2680
+ var BOOTSTRAP_EXIT_CODES = {
2681
+ SUCCESS: 0,
2682
+ FAIL: 1,
2683
+ ERROR: 3
2684
+ };
2685
+
2686
+ // src/engine/derive-engine.ts
2687
+ var import_promises3 = require("fs/promises");
2688
+
2689
+ // src/engine/derive-normalizer.ts
2690
+ function findSections(lines) {
2691
+ const sections = [];
2692
+ let current = null;
2693
+ for (let i = 0; i < lines.length; i++) {
2694
+ if (lines[i].startsWith("# ")) {
2695
+ if (current) {
2696
+ current.end = i;
2697
+ sections.push(current);
2698
+ }
2699
+ current = { name: lines[i].replace(/^#\s+/, "").trim(), start: i, end: lines.length };
2700
+ }
2701
+ }
2702
+ if (current) {
2703
+ current.end = lines.length;
2704
+ sections.push(current);
2705
+ }
2706
+ return sections;
2707
+ }
2708
+ var VALID_INVARIANT_RE = /^-\s+`[^`]+`\s*[—–-]\s*.+/;
2709
+ function normalizeInvariantLine(line) {
2710
+ const trimmed = line.trim();
2711
+ if (!trimmed.startsWith("- ")) return line;
2712
+ if (VALID_INVARIANT_RE.test(trimmed)) return line;
2713
+ const boldMatch = trimmed.match(/^-\s+\*\*([^*]+)\*\*\s*([—–-])\s*(.+)$/);
2714
+ if (boldMatch) {
2715
+ const id = boldMatch[1].toLowerCase().replace(/\s+/g, "_");
2716
+ const desc = boldMatch[3].trim();
2717
+ const hasParens = /\([^)]+\)\s*$/.test(desc);
2718
+ return `- \`${id}\` \u2014 ${hasParens ? desc : desc + " (structural, immutable)"}`;
2719
+ }
2720
+ const snakeMatch = trimmed.match(/^-\s+(\w[\w]*(?:_\w+)+)\s*([—–-])\s*(.+)$/);
2721
+ if (snakeMatch) {
2722
+ const id = snakeMatch[1];
2723
+ const desc = snakeMatch[3].trim();
2724
+ const hasParens = /\([^)]+\)\s*$/.test(desc);
2725
+ return `- \`${id}\` \u2014 ${hasParens ? desc : desc + " (structural, immutable)"}`;
2726
+ }
2727
+ const proseMatch = trimmed.match(/^-\s+([A-Z][^(]+?)(?:\s*\(([^)]+)\))?\s*$/);
2728
+ if (proseMatch) {
2729
+ const desc = proseMatch[1].trim();
2730
+ const parens = proseMatch[2] ?? "structural, immutable";
2731
+ const id = desc.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "_").slice(0, 50);
2732
+ if (id.length >= 3) {
2733
+ return `- \`${id}\` \u2014 ${desc} (${parens})`;
2734
+ }
2735
+ }
2736
+ return line;
2737
+ }
2738
+ var VALID_GATE_RE = /^-\s+\w+:\s*\w+\s*(==|!=|>=|<=|>|<)\s*[\d.]+/;
2739
+ var SYMBOLIC_TO_NUMERIC = {
2740
+ // Positive states
2741
+ full: 100,
2742
+ complete: 100,
2743
+ total: 100,
2744
+ optimal: 95,
2745
+ maximum: 100,
2746
+ high: 80,
2747
+ strong: 80,
2748
+ // Mid states
2749
+ partial: 60,
2750
+ moderate: 50,
2751
+ medium: 50,
2752
+ growing: 60,
2753
+ developing: 50,
2754
+ // Low/negative states
2755
+ low: 30,
2756
+ minimal: 20,
2757
+ weak: 20,
2758
+ declining: 40,
2759
+ limited: 30,
2760
+ escalating: 50,
2761
+ // Extreme states
2762
+ none: 0,
2763
+ absent: 0,
2764
+ zero: 0,
2765
+ critical: 80,
2766
+ extreme: 90,
2767
+ severe: 85,
2768
+ indiscriminate: 80,
2769
+ uncontrolled: 90,
2770
+ overwhelming: 90
2771
+ };
2772
+ function normalizeGateLine(line) {
2773
+ const trimmed = line.trim();
2774
+ if (!trimmed.startsWith("- ")) return line;
2775
+ if (VALID_GATE_RE.test(trimmed)) return line;
2776
+ const symbolicMatch = trimmed.match(
2777
+ /^-\s+(\w+):\s*(\w+)\s*(?:==?)\s*"?([a-zA-Z_]+)"?\s*$/
2778
+ );
2779
+ if (symbolicMatch) {
2780
+ const status = symbolicMatch[1];
2781
+ const field = symbolicMatch[2];
2782
+ const symbolic = symbolicMatch[3].toLowerCase();
2783
+ const numeric = SYMBOLIC_TO_NUMERIC[symbolic];
2784
+ if (numeric !== void 0) {
2785
+ return `- ${status}: ${field} >= ${numeric}`;
2786
+ }
2787
+ return `- ${status}: ${field} >= 50`;
2788
+ }
2789
+ const eqNumMatch = trimmed.match(/^-\s+(\w+):\s*(\w+)\s*=\s*([\d.]+)\s*$/);
2790
+ if (eqNumMatch) {
2791
+ return `- ${eqNumMatch[1]}: ${eqNumMatch[2]} >= ${eqNumMatch[3]}`;
2792
+ }
2793
+ return line;
2794
+ }
2795
+ function normalizeWhenLine(line) {
2796
+ const trimmed = line.trim();
2797
+ if (!trimmed.startsWith("When ")) return line;
2798
+ const parts = trimmed.slice(5).split(/\s+AND\s+/i);
2799
+ const normalized = parts.map((part) => {
2800
+ const p = part.trim();
2801
+ if (/\[(state|assumption)\]\s*$/.test(p)) return p;
2802
+ return p + " [state]";
2803
+ });
2804
+ return "When " + normalized.join(" AND ");
2805
+ }
2806
+ function normalizeWorldMarkdown(markdown) {
2807
+ const lines = markdown.split("\n");
2808
+ const sections = findSections(lines);
2809
+ const report = { fixCount: 0, invariantIds: 0, gateThresholds: 0, triggerTags: 0 };
2810
+ const invariantsSection = sections.find((s) => s.name.toLowerCase() === "invariants");
2811
+ const gatesSection = sections.find((s) => s.name.toLowerCase() === "gates");
2812
+ const rulesSection = sections.find((s) => s.name.toLowerCase() === "rules");
2813
+ for (let i = 0; i < lines.length; i++) {
2814
+ const original = lines[i];
2815
+ if (invariantsSection && i > invariantsSection.start && i < invariantsSection.end) {
2816
+ lines[i] = normalizeInvariantLine(lines[i]);
2817
+ if (lines[i] !== original) report.invariantIds++;
2818
+ }
2819
+ if (gatesSection && i > gatesSection.start && i < gatesSection.end) {
2820
+ lines[i] = normalizeGateLine(lines[i]);
2821
+ if (lines[i] !== original) report.gateThresholds++;
2822
+ }
2823
+ if (rulesSection && i > rulesSection.start && i < rulesSection.end) {
2824
+ lines[i] = normalizeWhenLine(lines[i]);
2825
+ if (lines[i] !== original) report.triggerTags++;
2826
+ }
2827
+ if (lines[i] !== original) report.fixCount++;
2828
+ }
2829
+ return { normalized: lines.join("\n"), fixCount: report.fixCount, report };
2830
+ }
2831
+
2832
+ // src/engine/derive-prompt.ts
2833
+ var import_promises = require("fs/promises");
2834
+ var import_path = require("path");
2835
+ var import_meta = {};
2836
+ var WORLD_FILENAME = "derivation-world.nv-world.md";
2837
+ function getModuleDir() {
2838
+ try {
2839
+ return (0, import_path.dirname)(new URL(import_meta.url).pathname);
2840
+ } catch {
2841
+ return __dirname;
2842
+ }
2843
+ }
2844
+ async function loadDerivationWorld() {
2845
+ const moduleDir = getModuleDir();
2846
+ const candidates = [
2847
+ (0, import_path.join)(moduleDir, "..", "worlds", WORLD_FILENAME),
2848
+ (0, import_path.join)(moduleDir, "worlds", WORLD_FILENAME)
2849
+ ];
2850
+ for (const candidate of candidates) {
2851
+ try {
2852
+ return await (0, import_promises.readFile)(candidate, "utf-8");
2853
+ } catch {
2854
+ }
2855
+ }
2856
+ throw new Error(
2857
+ `DerivationWorld not found. Searched:
2858
+ ${candidates.map((c2) => ` - ${c2}`).join("\n")}`
2859
+ );
2860
+ }
2861
+ async function collectMarkdownSources(inputPath) {
2862
+ const { stat, readFile: rf, readdir } = await import("fs/promises");
2863
+ const { join: pathJoin, extname, basename } = await import("path");
2864
+ const stats = await stat(inputPath);
2865
+ if (stats.isFile()) {
2866
+ const content = await rf(inputPath, "utf-8");
2867
+ return [{ filename: basename(inputPath), content }];
2868
+ }
2869
+ if (stats.isDirectory()) {
2870
+ const sources = [];
2871
+ await collectDir(inputPath, sources, rf, pathJoin, extname, basename);
2872
+ sources.sort((a, b) => a.filename.localeCompare(b.filename));
2873
+ return sources;
2874
+ }
2875
+ throw new Error(`Input path is neither a file nor a directory: ${inputPath}`);
2876
+ }
2877
+ async function collectDir(dir, sources, rf, pathJoin, extname, basename) {
2878
+ const { readdir } = await import("fs/promises");
2879
+ const entries = await readdir(dir, { withFileTypes: true });
2880
+ for (const entry of entries) {
2881
+ const fullPath = pathJoin(dir, entry.name);
2882
+ if (entry.isDirectory()) {
2883
+ await collectDir(fullPath, sources, rf, pathJoin, extname, basename);
2884
+ } else if (entry.isFile() && extname(entry.name).toLowerCase() === ".md") {
2885
+ const content = await rf(fullPath, "utf-8");
2886
+ sources.push({ filename: entry.name, content });
2887
+ }
2888
+ }
2889
+ }
2890
+ function concatenateSources(sources) {
2891
+ return sources.map((s) => `=== FILE: ${s.filename} ===
2892
+ ${s.content}`).join("\n\n");
2893
+ }
2894
+ var NV_WORLD_SPEC = `
2895
+ ## .nv-world.md Format Specification
2896
+
2897
+ A .nv-world.md file is a structured markdown document with YAML frontmatter and H1 sections.
2898
+
2899
+ ### Frontmatter (required)
2900
+ \`\`\`yaml
2901
+ ---
2902
+ world_id: <snake_case_id>
2903
+ name: <Human Readable Name>
2904
+ version: 1.0.0
2905
+ ---
2906
+ \`\`\`
2907
+
2908
+ ### Required Sections
2909
+
2910
+ # Thesis
2911
+ A single paragraph stating the structural claim this world tests. Must be testable and falsifiable.
2912
+
2913
+ # Invariants
2914
+ Bullet list of non-negotiable constraints.
2915
+
2916
+ STRICT FORMAT \u2014 Invariant IDs MUST be wrapped in backticks. Every bullet MUST follow this exact pattern:
2917
+ - \\\`invariant_id\\\` \u2014 Description text (enforcement, mutability)
2918
+
2919
+ VALID examples:
2920
+ - \\\`rage_manifestation\\\` \u2014 Suppressed rage manifests as a physical monster (structural, immutable)
2921
+ - \\\`healing_requires_integration\\\` \u2014 Recovery requires emotional integration (operational, immutable)
2922
+
2923
+ INVALID examples (these WILL cause parse failures):
2924
+ - rage_manifestation \u2014 Suppressed rage manifests as a monster
2925
+ - Suppressed rage manifests as a monster (structural, immutable)
2926
+ - **rage_manifestation** \u2014 Description
2927
+
2928
+ Rules:
2929
+ - The ID inside backticks must be snake_case (lowercase with underscores)
2930
+ - Use "structural" for constraints derived directly from source material
2931
+ - Use "operational" for constraints you inferred
2932
+ - Mutability is almost always "immutable"
2933
+
2934
+ # State
2935
+ H2 sub-sections, each defining a state variable:
2936
+ ## variable_name
2937
+ - type: number | enum | boolean
2938
+ - min: <number> (for number type)
2939
+ - max: <number> (for number type)
2940
+ - step: <number> (for number type)
2941
+ - options: option_a, option_b (for enum type)
2942
+ - default: <value>
2943
+ - label: Human Label
2944
+ - description: What this variable represents
2945
+
2946
+ IMPORTANT: State variable IDs (the ## heading) must be snake_case. The "default" value MUST be numeric for number types and MUST be one of the declared options for enum types.
2947
+
2948
+ # Assumptions
2949
+ H2 sub-sections, each defining a scenario profile:
2950
+ ## profile_id
2951
+ - name: Profile Name
2952
+ - description: What this profile represents
2953
+ - param_key: param_value
2954
+
2955
+ # Rules
2956
+ H2 sub-sections with this format:
2957
+ ## rule-001: Rule Label (structural|degradation|advantage)
2958
+ Single sentence description.
2959
+
2960
+ When field == "value" [state] AND other_field > 50 [state]
2961
+ Then target *= 0.30, other_target = false
2962
+ Collapse: field < 0.05
2963
+
2964
+ > trigger: What triggers this rule
2965
+ > rule: What the rule means
2966
+ > shift: What changes
2967
+ > effect: The concrete effect
2968
+
2969
+ Rules must have:
2970
+ - A "When" line with triggers referencing [state] or [assumption] sources
2971
+ - A "Then" line with effects using =, *=, +=, or -= operators
2972
+ - Optional "Collapse:" line for failure conditions
2973
+
2974
+ STRICT FORMAT for triggers \u2014 each trigger MUST match: field_name <operator> <value> [state|assumption]
2975
+ - Operators: ==, !=, >=, <=, >, <
2976
+ - Values must be numeric (50), boolean (true/false), or quoted strings ("value")
2977
+ - Source tag [state] or [assumption] is REQUIRED
2978
+
2979
+ STRICT FORMAT for effects \u2014 each effect MUST match: target_field <operator> <value>
2980
+ - Operators: =, *=, +=, -=
2981
+ - Values must be numeric (0.30), boolean (true/false), or quoted strings ("value")
2982
+
2983
+ # Gates
2984
+ Bullet list of status thresholds (must be monotonically decreasing).
2985
+
2986
+ STRICT FORMAT \u2014 Gate thresholds MUST be numeric. Symbolic values like "full", "partial", "escalating" are NOT allowed. Every gate MUST follow this exact pattern:
2987
+ - STATUS_NAME: field_name >= <number>
2988
+
2989
+ VALID examples:
2990
+ - BEST_STATUS: integration_level >= 90
2991
+ - GOOD_STATUS: integration_level >= 60
2992
+ - WARN_STATUS: monster_rage >= 40
2993
+ - BAD_STATUS: monster_rage >= 70
2994
+ - WORST_STATUS: josie_endangerment >= 90
2995
+
2996
+ INVALID examples (these WILL cause parse failures):
2997
+ - BEST_STATUS: integration = full
2998
+ - GOOD_STATUS: integration = partial
2999
+ - BEST_STATUS: true_integration_state == "complete"
3000
+
3001
+ Rules:
3002
+ - All gate thresholds must be NUMERIC values (integers or decimals)
3003
+ - The field_name must reference a declared State variable or Outcome
3004
+ - Operators: >=, <=, >, <, ==, !=
3005
+ - Status names should be uppercase with underscores
3006
+ - Thresholds must be monotonically decreasing from best to worst status
3007
+
3008
+ # Outcomes
3009
+ H2 sub-sections defining computed outcomes:
3010
+ ## outcome_id
3011
+ - type: number | enum | boolean
3012
+ - range: 0-100 (for numbers)
3013
+ - display: percentage | integer | decimal
3014
+ - label: Human Label
3015
+ - primary: true (for the main metric)
3016
+ `.trim();
3017
+ async function buildSystemPrompt() {
3018
+ const derivationWorld = await loadDerivationWorld();
3019
+ const invariantLines = derivationWorld.split("\n").filter((l) => l.trim().startsWith("- `")).map((l) => {
3020
+ const match = l.match(/^-\s+`([^`]+)`\s*[—–-]\s*(.+?)(?:\s*\([^)]+\))?\s*$/);
3021
+ return match ? `- ${match[2].trim()}` : null;
3022
+ }).filter(Boolean);
3023
+ return `You are a governance document synthesizer for NeuroVerse OS.
3024
+
3025
+ Your task: Given arbitrary markdown source material, synthesize a valid .nv-world.md governance document.
3026
+
3027
+ ${NV_WORLD_SPEC}
3028
+
3029
+ ## Synthesis Constraints (from DerivationWorld)
3030
+
3031
+ You MUST follow these constraints:
3032
+ ${invariantLines.join("\n")}
3033
+
3034
+ ## Critical Rules
3035
+
3036
+ 1. Output ONLY the .nv-world.md document. No preamble, no explanation, no trailing commentary.
3037
+ 2. Do not wrap the output in a code fence.
3038
+ 3. Every invariant derived from explicit source statements must be marked (structural, immutable).
3039
+ 4. Every invariant you infer that is not directly stated in the source must be marked (operational, immutable).
3040
+ 5. All state variables, rules, and invariants must trace to concepts present in the input.
3041
+ 6. Do not invent governance domains beyond what the source material covers.
3042
+ 7. Every rule must have a When trigger line and a Then effect line.
3043
+ 8. Gate thresholds must be monotonically decreasing from best to worst status.
3044
+ 9. The frontmatter must include world_id, name, and version.
3045
+ 10. Rule descriptions must be a single sentence.
3046
+
3047
+ ## Syntax Precision (CRITICAL)
3048
+
3049
+ The output is parsed by a deterministic regex parser. If the syntax is wrong, the parse FAILS.
3050
+
3051
+ INVARIANTS: Every invariant bullet MUST use backtick-wrapped IDs:
3052
+ CORRECT: - \\\`my_invariant_id\\\` \u2014 Description (structural, immutable)
3053
+ WRONG: - my_invariant_id \u2014 Description
3054
+
3055
+ GATES: Every gate MUST use NUMERIC thresholds:
3056
+ CORRECT: - THRIVING: score >= 80
3057
+ WRONG: - THRIVING: state = optimal
3058
+
3059
+ TRIGGERS: Every trigger MUST end with [state] or [assumption]:
3060
+ CORRECT: When score < 25 [state]
3061
+ WRONG: When score < 25
3062
+
3063
+ EFFECTS: Every effect MUST use a recognized operator:
3064
+ CORRECT: Then score *= 0.50, active = false
3065
+ WRONG: Then score decreases by half`;
3066
+ }
3067
+ function buildUserPrompt(concatenatedMarkdown) {
3068
+ return `Synthesize a valid .nv-world.md under DerivationWorld constraints from the following source material.
3069
+
3070
+ Output ONLY the .nv-world.md content. Start with the --- frontmatter delimiter.
3071
+
3072
+ Source material:
3073
+
3074
+ ${concatenatedMarkdown}`;
3075
+ }
3076
+
3077
+ // src/providers/ai-provider.ts
3078
+ var ChatCompletionsProvider = class {
3079
+ model;
3080
+ apiKey;
3081
+ endpoint;
3082
+ constructor(config) {
3083
+ this.model = config.model;
3084
+ this.apiKey = config.apiKey;
3085
+ this.endpoint = config.endpoint ?? "https://api.openai.com/v1/chat/completions";
3086
+ }
3087
+ async complete(systemPrompt, userPrompt) {
3088
+ const body = {
3089
+ model: this.model,
3090
+ messages: [
3091
+ { role: "system", content: systemPrompt },
3092
+ { role: "user", content: userPrompt }
3093
+ ],
3094
+ temperature: 0.2,
3095
+ max_tokens: 16384
3096
+ };
3097
+ const response = await fetch(this.endpoint, {
3098
+ method: "POST",
3099
+ headers: {
3100
+ "Content-Type": "application/json",
3101
+ "Authorization": `Bearer ${this.apiKey}`
3102
+ },
3103
+ body: JSON.stringify(body)
3104
+ });
3105
+ if (!response.ok) {
3106
+ const text = await response.text().catch(() => "");
3107
+ throw new Error(`Provider returned ${response.status}: ${text.slice(0, 200)}`);
3108
+ }
3109
+ const data = await response.json();
3110
+ const content = data?.choices?.[0]?.message?.content;
3111
+ if (!content) {
3112
+ throw new Error("Provider returned empty response \u2014 no content in choices[0].message.content");
3113
+ }
3114
+ return content;
3115
+ }
3116
+ };
3117
+ function createProvider(config) {
3118
+ return new ChatCompletionsProvider(config);
3119
+ }
3120
+
3121
+ // src/providers/config-manager.ts
3122
+ var import_promises2 = require("fs/promises");
3123
+ var import_path2 = require("path");
3124
+ var import_os = require("os");
3125
+ function getConfigDir() {
3126
+ const xdg = process.env.XDG_CONFIG_HOME;
3127
+ if (xdg) return (0, import_path2.join)(xdg, "neuroverse");
3128
+ return (0, import_path2.join)((0, import_os.homedir)(), ".neuroverse");
3129
+ }
3130
+ function getConfigPath() {
3131
+ return (0, import_path2.join)(getConfigDir(), "config.json");
3132
+ }
3133
+ async function loadConfig() {
3134
+ try {
3135
+ const raw = await (0, import_promises2.readFile)(getConfigPath(), "utf-8");
3136
+ const parsed = JSON.parse(raw);
3137
+ if (!parsed.provider || !parsed.model || !parsed.apiKey) {
3138
+ return null;
3139
+ }
3140
+ return {
3141
+ provider: parsed.provider,
3142
+ model: parsed.model,
3143
+ apiKey: parsed.apiKey,
3144
+ endpoint: parsed.endpoint ?? null
3145
+ };
3146
+ } catch {
3147
+ return null;
3148
+ }
3149
+ }
3150
+
3151
+ // src/engine/derive-engine.ts
3152
+ function extractWorldMarkdown(raw) {
3153
+ let content = raw.trim();
3154
+ const fenceMatch = content.match(/```(?:markdown|md)?\s*\n([\s\S]*?)```/);
3155
+ if (fenceMatch) {
3156
+ content = fenceMatch[1].trim();
3157
+ }
3158
+ const sentinelMatch = content.match(
3159
+ /(?:BEGIN NV WORLD|BEGIN_NV_WORLD)[\s\n]*([\s\S]*?)[\s\n]*(?:END NV WORLD|END_NV_WORLD)/i
3160
+ );
3161
+ if (sentinelMatch) {
3162
+ content = sentinelMatch[1].trim();
3163
+ }
3164
+ if (!content.startsWith("---")) {
3165
+ const fmIndex = content.indexOf("---\n");
3166
+ if (fmIndex >= 0) {
3167
+ content = content.slice(fmIndex);
3168
+ }
3169
+ }
3170
+ if (!content.startsWith("---") && /^# /m.test(content)) {
3171
+ const generated = generateFrontmatter(content);
3172
+ content = generated + "\n" + content;
3173
+ }
3174
+ if (!content.startsWith("---")) return null;
3175
+ const fmEnd = content.indexOf("---", 3);
3176
+ if (fmEnd === -1) return null;
3177
+ const frontmatter = content.slice(3, fmEnd);
3178
+ if (!frontmatter.includes("world_id:")) return null;
3179
+ return content;
3180
+ }
3181
+ function generateFrontmatter(content) {
3182
+ let name = "Derived World";
3183
+ const thesisMatch = content.match(/^# Thesis\s*\n+(.+)/m);
3184
+ if (thesisMatch) {
3185
+ const firstSentence = thesisMatch[1].replace(/[.!?].*$/, "").trim();
3186
+ if (firstSentence.length >= 3 && firstSentence.length <= 60) {
3187
+ name = firstSentence;
3188
+ }
3189
+ }
3190
+ const worldId = name.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "_").slice(0, 50);
3191
+ return [
3192
+ "---",
3193
+ `world_id: ${worldId}`,
3194
+ `name: ${name}`,
3195
+ "version: 0.1",
3196
+ "runtime_mode: simulation",
3197
+ "---"
3198
+ ].join("\n");
3199
+ }
3200
+ var GATES = [
3201
+ { status: "FAITHFUL", threshold: 0.85 },
3202
+ { status: "USABLE", threshold: 0.6 },
3203
+ { status: "REVIEWABLE", threshold: 0.4 },
3204
+ { status: "SUSPECT", threshold: 0.15 },
3205
+ { status: "DERIVATION_REJECTED", threshold: 0 }
3206
+ ];
3207
+ function classifyGate(errors, warnings, sectionCount) {
3208
+ const maxSections = 7;
3209
+ const completeness = sectionCount / maxSections;
3210
+ const fidelity = errors === 0 ? Math.max(0, completeness - warnings * 0.05) : 0;
3211
+ for (const gate of GATES) {
3212
+ if (fidelity >= gate.threshold) return gate.status;
3213
+ }
3214
+ return "DERIVATION_REJECTED";
3215
+ }
3216
+ async function deriveWorld(options) {
3217
+ const startTime = performance.now();
3218
+ const sources = await collectMarkdownSources(options.inputPath);
3219
+ if (sources.length === 0) {
3220
+ throw new DeriveInputError("No markdown files found in input path");
3221
+ }
3222
+ const concatenated = concatenateSources(sources);
3223
+ const systemPrompt = await buildSystemPrompt();
3224
+ const userPrompt = buildUserPrompt(concatenated);
3225
+ if (options.dryRun) {
3226
+ return {
3227
+ result: {
3228
+ success: true,
3229
+ outputPath: options.outputPath,
3230
+ sectionsDetected: [],
3231
+ validationErrors: 0,
3232
+ validationWarnings: 0,
3233
+ findings: [],
3234
+ gate: "DRY_RUN",
3235
+ durationMs: performance.now() - startTime
3236
+ },
3237
+ exitCode: 0,
3238
+ dryRunOutput: { systemPrompt, userPrompt }
3239
+ };
3240
+ }
3241
+ const savedConfig = await loadConfig();
3242
+ const config = {
3243
+ provider: options.providerOverride?.provider ?? savedConfig?.provider ?? "",
3244
+ model: options.providerOverride?.model ?? savedConfig?.model ?? "",
3245
+ apiKey: options.providerOverride?.apiKey ?? savedConfig?.apiKey ?? "",
3246
+ endpoint: options.providerOverride?.endpoint ?? savedConfig?.endpoint ?? null
3247
+ };
3248
+ if (!config.apiKey) {
3249
+ throw new DeriveProviderError("No API key configured. Run: neuroverse configure-ai");
3250
+ }
3251
+ if (!config.model) {
3252
+ throw new DeriveProviderError("No model configured. Run: neuroverse configure-ai");
3253
+ }
3254
+ const provider = createProvider(config);
3255
+ let rawResponse;
3256
+ try {
3257
+ rawResponse = await provider.complete(systemPrompt, userPrompt);
3258
+ } catch (e) {
3259
+ throw new DeriveProviderError(`AI provider failed: ${e instanceof Error ? e.message : String(e)}`);
3260
+ }
3261
+ const extracted = extractWorldMarkdown(rawResponse);
3262
+ if (!extracted) {
3263
+ throw new DeriveProviderError(
3264
+ "Could not extract valid .nv-world.md from AI response. Response must contain frontmatter with world_id."
3265
+ );
3266
+ }
3267
+ const { normalized, fixCount, report: normReport } = normalizeWorldMarkdown(extracted);
3268
+ const { world, issues } = parseWorldMarkdown(normalized);
3269
+ const errors = issues.filter((i) => i.severity === "error");
3270
+ const warnings = issues.filter((i) => i.severity === "warning");
3271
+ const sectionsDetected = world ? Object.keys(world).filter((k) => {
3272
+ const val = world[k];
3273
+ if (Array.isArray(val)) return val.length > 0;
3274
+ if (typeof val === "string") return val.length > 0;
3275
+ return !!val;
3276
+ }) : [];
3277
+ if (world && options.validate) {
3278
+ try {
3279
+ const { world: worldDef } = emitWorldDefinition(world);
3280
+ const report = validateWorld(worldDef);
3281
+ for (const finding2 of report.findings) {
3282
+ if (finding2.severity === "error" || finding2.severity === "warning") {
3283
+ issues.push({
3284
+ line: 0,
3285
+ section: `Validate:${finding2.category}`,
3286
+ message: finding2.message,
3287
+ severity: finding2.severity
3288
+ });
3289
+ }
3290
+ }
3291
+ } catch {
3292
+ }
3293
+ }
3294
+ const allErrors = issues.filter((i) => i.severity === "error");
3295
+ const allWarnings = issues.filter((i) => i.severity === "warning");
3296
+ const gate = classifyGate(allErrors.length, allWarnings.length, sectionsDetected.length);
3297
+ const findings = issues.filter((i) => i.severity === "error" || i.severity === "warning").map((i) => ({
3298
+ severity: i.severity,
3299
+ section: i.section,
3300
+ message: i.message,
3301
+ line: i.line
3302
+ }));
3303
+ let output = normalized;
3304
+ if (findings.length > 0 || fixCount > 0) {
3305
+ const lines = [`<!-- DERIVATION STATUS: ${gate}`];
3306
+ if (fixCount > 0) {
3307
+ const details = [];
3308
+ if (normReport.invariantIds > 0) details.push(`${normReport.invariantIds} invariant ID(s) wrapped`);
3309
+ if (normReport.gateThresholds > 0) details.push(`${normReport.gateThresholds} gate threshold(s) converted`);
3310
+ if (normReport.triggerTags > 0) details.push(`${normReport.triggerTags} trigger(s) tagged with [state]`);
3311
+ lines.push(``, `Normalizer: ${fixCount} fix(es) applied`);
3312
+ for (const d of details) lines.push(` - ${d}`);
3313
+ }
3314
+ const errs = findings.filter((f) => f.severity === "error");
3315
+ const warns = findings.filter((f) => f.severity === "warning");
3316
+ if (errs.length > 0) {
3317
+ lines.push("", "Errors:");
3318
+ for (const f of errs) lines.push(`- [${f.section}] ${f.message}`);
3319
+ }
3320
+ if (warns.length > 0) {
3321
+ lines.push("", "Warnings:");
3322
+ for (const f of warns) lines.push(`- [${f.section}] ${f.message}`);
3323
+ }
3324
+ lines.push("-->", "");
3325
+ output = lines.join("\n") + normalized;
3326
+ }
3327
+ await (0, import_promises3.writeFile)(options.outputPath, output, "utf-8");
3328
+ const hasErrors = allErrors.length > 0;
3329
+ return {
3330
+ result: {
3331
+ success: !hasErrors,
3332
+ outputPath: options.outputPath,
3333
+ sectionsDetected,
3334
+ validationErrors: allErrors.length,
3335
+ validationWarnings: allWarnings.length,
3336
+ findings,
3337
+ gate,
3338
+ normalization: fixCount > 0 ? {
3339
+ fixCount: normReport.fixCount,
3340
+ invariantIds: normReport.invariantIds,
3341
+ gateThresholds: normReport.gateThresholds,
3342
+ triggerTags: normReport.triggerTags
3343
+ } : void 0,
3344
+ durationMs: performance.now() - startTime
3345
+ },
3346
+ exitCode: hasErrors ? 1 : 0
3347
+ };
3348
+ }
3349
+ var DeriveInputError = class extends Error {
3350
+ constructor(message) {
3351
+ super(message);
3352
+ this.name = "DeriveInputError";
3353
+ }
3354
+ };
3355
+ var DeriveProviderError = class extends Error {
3356
+ constructor(message) {
3357
+ super(message);
3358
+ this.name = "DeriveProviderError";
3359
+ }
3360
+ };
3361
+
3362
+ // src/engine/explain-engine.ts
3363
+ function detectTensions(rules) {
3364
+ const increases = /* @__PURE__ */ new Map();
3365
+ const decreases = /* @__PURE__ */ new Map();
3366
+ for (const rule of rules) {
3367
+ const allEffects = collectAllEffects(rule);
3368
+ for (const effect of allEffects) {
3369
+ const target = effect.target;
3370
+ const isIncrease = effect.operation === "add" || effect.operation === "multiply" && typeof effect.value === "number" && effect.value > 1;
3371
+ const isDecrease = effect.operation === "subtract" || effect.operation === "multiply" && typeof effect.value === "number" && effect.value < 1;
3372
+ if (isIncrease) {
3373
+ if (!increases.has(target)) increases.set(target, []);
3374
+ increases.get(target).push(rule.label);
3375
+ }
3376
+ if (isDecrease) {
3377
+ if (!decreases.has(target)) decreases.set(target, []);
3378
+ decreases.get(target).push(rule.label);
3379
+ }
3380
+ }
3381
+ }
3382
+ const tensions = [];
3383
+ for (const [variable, incRules] of increases) {
3384
+ const decRules = decreases.get(variable);
3385
+ if (decRules && decRules.length > 0) {
3386
+ tensions.push({
3387
+ variable,
3388
+ increasedBy: incRules,
3389
+ decreasedBy: decRules
3390
+ });
3391
+ }
3392
+ }
3393
+ return tensions;
3394
+ }
3395
+ function collectAllEffects(rule) {
3396
+ const effects = [...rule.effects ?? []];
3397
+ for (const ce of rule.effects_conditional ?? []) {
3398
+ effects.push(...ce.effects);
3399
+ }
3400
+ return effects;
3401
+ }
3402
+ function explainWorld(world) {
3403
+ const dynamics = [...world.rules].sort((a, b) => a.order - b.order).map((rule) => {
3404
+ const allEffects = collectAllEffects(rule);
3405
+ return {
3406
+ ruleId: rule.id,
3407
+ label: rule.label,
3408
+ severity: rule.severity,
3409
+ triggerDescription: rule.causal_translation?.trigger_text ?? describeTriggers(rule),
3410
+ effectDescription: rule.causal_translation?.effect_text ?? describeEffects(allEffects),
3411
+ targets: [...new Set(allEffects.map((e) => e.target))]
3412
+ };
3413
+ });
3414
+ const stateVariables = Object.entries(world.stateSchema.variables ?? {}).map(
3415
+ ([name, v]) => ({
3416
+ name,
3417
+ label: v.label || name,
3418
+ type: v.type,
3419
+ defaultValue: v.default,
3420
+ range: v.type === "number" && v.min !== void 0 && v.max !== void 0 ? `${v.min}\u2013${v.max}` : v.type === "enum" && v.options ? v.options.join(", ") : void 0
3421
+ })
3422
+ );
3423
+ const invariants = world.invariants.map((inv) => ({
3424
+ id: inv.id,
3425
+ label: inv.label,
3426
+ enforcement: inv.enforcement
3427
+ }));
3428
+ const gates = (world.gates.viability_classification ?? []).map((g) => ({
3429
+ status: g.status,
3430
+ field: g.field,
3431
+ threshold: `${g.operator} ${g.value}`
3432
+ }));
3433
+ const tensions = detectTensions(world.rules);
3434
+ const outcomes = (world.outcomes.computed_outcomes ?? []).map((o) => ({
3435
+ id: o.id,
3436
+ label: o.label,
3437
+ type: o.type,
3438
+ primary: o.primary ?? false
3439
+ }));
3440
+ const stats = {
3441
+ invariants: world.invariants.length,
3442
+ stateVariables: Object.keys(world.stateSchema.variables ?? {}).length,
3443
+ rules: world.rules.length,
3444
+ gates: (world.gates.viability_classification ?? []).length,
3445
+ outcomes: (world.outcomes.computed_outcomes ?? []).length,
3446
+ assumptions: Object.keys(world.assumptions.profiles ?? {}).length
3447
+ };
3448
+ return {
3449
+ worldName: world.world.name,
3450
+ worldId: world.world.world_id,
3451
+ thesis: world.world.thesis,
3452
+ dynamics,
3453
+ stateVariables,
3454
+ invariants,
3455
+ gates,
3456
+ tensions,
3457
+ outcomes,
3458
+ stats
3459
+ };
3460
+ }
3461
+ function describeTriggers(rule) {
3462
+ if (!rule.triggers || rule.triggers.length === 0) return "Always active";
3463
+ return rule.triggers.map((t) => `${t.field} ${t.operator} ${t.value}`).join(" AND ");
3464
+ }
3465
+ function describeEffects(effects) {
3466
+ if (effects.length === 0) return "No direct effects";
3467
+ return effects.map((e) => {
3468
+ switch (e.operation) {
3469
+ case "multiply":
3470
+ return `${e.target} scaled by ${e.value}`;
3471
+ case "add":
3472
+ return `${e.target} increased by ${e.value}`;
3473
+ case "subtract":
3474
+ return `${e.target} decreased by ${e.value}`;
3475
+ case "set":
3476
+ return `${e.target} set to ${e.value}`;
3477
+ case "set_boolean":
3478
+ return `${e.target} set to ${e.value}`;
3479
+ default:
3480
+ return `${e.target} ${e.operation} ${e.value}`;
3481
+ }
3482
+ }).join("; ");
3483
+ }
3484
+ function renderExplainText(output) {
3485
+ const lines = [];
3486
+ lines.push(`WORLD: ${output.worldName}`);
3487
+ lines.push(`ID: ${output.worldId}`);
3488
+ lines.push("");
3489
+ if (output.thesis) {
3490
+ lines.push("THESIS");
3491
+ lines.push(` ${output.thesis}`);
3492
+ lines.push("");
3493
+ }
3494
+ lines.push("STRUCTURE");
3495
+ lines.push(` ${output.stats.invariants} invariants, ${output.stats.stateVariables} state variables, ${output.stats.rules} rules`);
3496
+ lines.push(` ${output.stats.gates} viability gates, ${output.stats.outcomes} outcomes, ${output.stats.assumptions} assumption profiles`);
3497
+ lines.push("");
3498
+ if (output.dynamics.length > 0) {
3499
+ lines.push("KEY DYNAMICS");
3500
+ for (const d of output.dynamics) {
3501
+ const severity = d.severity === "structural" ? "[structural]" : d.severity === "advantage" ? "[advantage]" : "[degradation]";
3502
+ lines.push(` ${d.label} ${severity}`);
3503
+ lines.push(` When: ${d.triggerDescription}`);
3504
+ lines.push(` Then: ${d.effectDescription}`);
3505
+ }
3506
+ lines.push("");
3507
+ }
3508
+ if (output.tensions.length > 0) {
3509
+ lines.push("DRAMATIC TENSIONS");
3510
+ for (const t of output.tensions) {
3511
+ lines.push(` ${t.variable}:`);
3512
+ lines.push(` Increased by: ${t.increasedBy.join(", ")}`);
3513
+ lines.push(` Decreased by: ${t.decreasedBy.join(", ")}`);
3514
+ }
3515
+ lines.push("");
3516
+ }
3517
+ if (output.stateVariables.length > 0) {
3518
+ lines.push("STATE VARIABLES");
3519
+ for (const v of output.stateVariables) {
3520
+ const range = v.range ? ` (${v.range})` : "";
3521
+ lines.push(` ${v.label}: ${v.type}, default ${v.defaultValue}${range}`);
3522
+ }
3523
+ lines.push("");
3524
+ }
3525
+ if (output.invariants.length > 0) {
3526
+ lines.push("INVARIANTS (cannot change)");
3527
+ for (const inv of output.invariants) {
3528
+ lines.push(` ${inv.label} [${inv.enforcement}]`);
3529
+ }
3530
+ lines.push("");
3531
+ }
3532
+ if (output.gates.length > 0) {
3533
+ lines.push("VIABILITY GATES");
3534
+ for (const g of output.gates) {
3535
+ lines.push(` ${g.status}: ${g.field} ${g.threshold}`);
3536
+ }
3537
+ lines.push("");
3538
+ }
3539
+ if (output.outcomes.length > 0) {
3540
+ lines.push("OUTCOMES");
3541
+ for (const o of output.outcomes) {
3542
+ const primary = o.primary ? " [primary]" : "";
3543
+ lines.push(` ${o.label} (${o.type})${primary}`);
3544
+ }
3545
+ lines.push("");
3546
+ }
3547
+ return lines.join("\n");
3548
+ }
3549
+
3550
+ // src/engine/simulate-engine.ts
3551
+ function simulateWorld(world, options = {}) {
3552
+ const steps = Math.max(1, Math.min(options.steps ?? 1, 50));
3553
+ const profileName = options.profile ?? world.world.default_assumption_profile;
3554
+ const state = buildInitialState(world.stateSchema, options.stateOverrides);
3555
+ const assumptions = resolveAssumptions(world.assumptions, profileName);
3556
+ const initialState = { ...state };
3557
+ const simulationSteps = [];
3558
+ let collapsed = false;
3559
+ let collapseStep;
3560
+ let collapseRule;
3561
+ const sortedRules = [...world.rules].sort((a, b) => a.order - b.order);
3562
+ for (let stepNum = 1; stepNum <= steps; stepNum++) {
3563
+ if (collapsed) break;
3564
+ const stepResult = evaluateStep(
3565
+ stepNum,
3566
+ sortedRules,
3567
+ state,
3568
+ assumptions,
3569
+ world
3570
+ );
3571
+ simulationSteps.push(stepResult);
3572
+ if (stepResult.collapsed) {
3573
+ collapsed = true;
3574
+ collapseStep = stepNum;
3575
+ collapseRule = stepResult.rulesEvaluated.find((r) => r.collapsed)?.ruleId;
3576
+ }
3577
+ }
3578
+ const finalViability = classifyViability(state, world);
3579
+ return {
3580
+ worldId: world.world.world_id,
3581
+ worldName: world.world.name,
3582
+ profile: profileName,
3583
+ initialState,
3584
+ steps: simulationSteps,
3585
+ finalState: { ...state },
3586
+ finalViability,
3587
+ collapsed,
3588
+ collapseStep,
3589
+ collapseRule
3590
+ };
3591
+ }
3592
+ function evaluateStep(stepNum, rules, state, assumptions, world) {
3593
+ const evaluations = [];
3594
+ let rulesFired = 0;
3595
+ let collapsed = false;
3596
+ const firedRuleIds = /* @__PURE__ */ new Set();
3597
+ for (const rule of rules) {
3598
+ if (collapsed) {
3599
+ evaluations.push({
3600
+ ruleId: rule.id,
3601
+ label: rule.label,
3602
+ triggered: false,
3603
+ excluded: true,
3604
+ effects: [],
3605
+ collapsed: false
3606
+ });
3607
+ continue;
3608
+ }
3609
+ const excluded = rule.exclusive_with ? firedRuleIds.has(rule.exclusive_with) : false;
3610
+ if (excluded) {
3611
+ evaluations.push({
3612
+ ruleId: rule.id,
3613
+ label: rule.label,
3614
+ triggered: false,
3615
+ excluded: true,
3616
+ effects: [],
3617
+ collapsed: false
3618
+ });
3619
+ continue;
3620
+ }
3621
+ const triggered = evaluateTriggers(rule.triggers, state, assumptions);
3622
+ if (!triggered) {
3623
+ evaluations.push({
3624
+ ruleId: rule.id,
3625
+ label: rule.label,
3626
+ triggered: false,
3627
+ excluded: false,
3628
+ effects: [],
3629
+ collapsed: false
3630
+ });
3631
+ continue;
3632
+ }
3633
+ firedRuleIds.add(rule.id);
3634
+ rulesFired++;
3635
+ const appliedEffects = [];
3636
+ for (const effect of rule.effects ?? []) {
3637
+ const applied = applyEffect(effect, state);
3638
+ if (applied) appliedEffects.push(applied);
3639
+ }
3640
+ for (const ce of rule.effects_conditional ?? []) {
3641
+ const conditionMet = evaluateSingleTrigger(ce.condition, state, assumptions);
3642
+ const andMet = ce.and ? evaluateSingleTrigger(ce.and, state, assumptions) : true;
3643
+ const orMet = ce.or ? evaluateSingleTrigger(ce.or, state, assumptions) : false;
3644
+ const anyMet = ce.condition_any ? ce.condition_any.some((c2) => evaluateSingleTrigger(c2, state, assumptions)) : false;
3645
+ const shouldApply = conditionMet && andMet || ce.or && orMet || ce.condition_any && anyMet;
3646
+ if (shouldApply) {
3647
+ for (const effect of ce.effects) {
3648
+ const applied = applyEffect(effect, state);
3649
+ if (applied) appliedEffects.push(applied);
3650
+ }
3651
+ }
3652
+ }
3653
+ let ruleCollapsed = false;
3654
+ if (rule.collapse_check) {
3655
+ const fieldVal = typeof state[rule.collapse_check.field] === "number" ? state[rule.collapse_check.field] : 0;
3656
+ if (evaluateOperator(fieldVal, rule.collapse_check.operator, rule.collapse_check.value)) {
3657
+ ruleCollapsed = true;
3658
+ collapsed = true;
3659
+ }
3660
+ }
3661
+ if (!ruleCollapsed && rule.secondary_check) {
3662
+ const fieldVal = typeof state[rule.secondary_check.field] === "number" ? state[rule.secondary_check.field] : 0;
3663
+ if (evaluateOperator(fieldVal, rule.secondary_check.operator, rule.secondary_check.value)) {
3664
+ ruleCollapsed = true;
3665
+ collapsed = true;
3666
+ }
3667
+ }
3668
+ evaluations.push({
3669
+ ruleId: rule.id,
3670
+ label: rule.label,
3671
+ triggered: true,
3672
+ excluded: false,
3673
+ effects: appliedEffects,
3674
+ collapsed: ruleCollapsed,
3675
+ collapseField: ruleCollapsed ? rule.collapse_check?.field ?? rule.secondary_check?.field : void 0
3676
+ });
3677
+ }
3678
+ const viability = classifyViability(state, world);
3679
+ return {
3680
+ step: stepNum,
3681
+ rulesEvaluated: evaluations,
3682
+ rulesFired,
3683
+ stateAfter: { ...state },
3684
+ viability,
3685
+ collapsed
3686
+ };
3687
+ }
3688
+ function evaluateTriggers(triggers, state, assumptions) {
3689
+ if (!triggers || triggers.length === 0) return true;
3690
+ return triggers.every((t) => evaluateSingleTrigger(t, state, assumptions));
3691
+ }
3692
+ function evaluateSingleTrigger(trigger, state, assumptions) {
3693
+ const source = trigger.source === "assumption" ? assumptions : state;
3694
+ const fieldValue = source[trigger.field];
3695
+ if (fieldValue === void 0) return false;
3696
+ return evaluateOperator(fieldValue, trigger.operator, trigger.value);
3697
+ }
3698
+ function evaluateOperator(fieldValue, operator, conditionValue) {
3699
+ switch (operator) {
3700
+ case "==":
3701
+ return fieldValue === conditionValue;
3702
+ case "!=":
3703
+ return fieldValue !== conditionValue;
3704
+ case ">":
3705
+ return typeof fieldValue === "number" && typeof conditionValue === "number" && fieldValue > conditionValue;
3706
+ case "<":
3707
+ return typeof fieldValue === "number" && typeof conditionValue === "number" && fieldValue < conditionValue;
3708
+ case ">=":
3709
+ return typeof fieldValue === "number" && typeof conditionValue === "number" && fieldValue >= conditionValue;
3710
+ case "<=":
3711
+ return typeof fieldValue === "number" && typeof conditionValue === "number" && fieldValue <= conditionValue;
3712
+ case "in":
3713
+ return Array.isArray(conditionValue) && conditionValue.includes(String(fieldValue));
3714
+ default:
3715
+ return false;
3716
+ }
3717
+ }
3718
+ function applyEffect(effect, state) {
3719
+ const before = state[effect.target];
3720
+ let after;
3721
+ switch (effect.operation) {
3722
+ case "multiply":
3723
+ case "multiply_dynamic": {
3724
+ const current = typeof before === "number" ? before : 0;
3725
+ const factor = typeof effect.value === "number" ? effect.value : 1;
3726
+ after = current * factor;
3727
+ break;
3728
+ }
3729
+ case "add":
3730
+ case "add_dynamic": {
3731
+ const current = typeof before === "number" ? before : 0;
3732
+ const addend = typeof effect.value === "number" ? effect.value : 0;
3733
+ after = current + addend;
3734
+ break;
3735
+ }
3736
+ case "subtract":
3737
+ case "subtract_dynamic": {
3738
+ const current = typeof before === "number" ? before : 0;
3739
+ const subtrahend = typeof effect.value === "number" ? effect.value : 0;
3740
+ after = current - subtrahend;
3741
+ break;
3742
+ }
3743
+ case "set":
3744
+ case "set_dynamic":
3745
+ after = effect.value;
3746
+ break;
3747
+ case "set_boolean":
3748
+ after = !!effect.value;
3749
+ break;
3750
+ default:
3751
+ return null;
3752
+ }
3753
+ state[effect.target] = after;
3754
+ return {
3755
+ target: effect.target,
3756
+ operation: effect.operation,
3757
+ value: effect.value,
3758
+ before: before ?? 0,
3759
+ after
3760
+ };
3761
+ }
3762
+ function buildInitialState(schema, overrides) {
3763
+ const state = {};
3764
+ for (const [name, variable] of Object.entries(schema.variables ?? {})) {
3765
+ state[name] = variable.default;
3766
+ }
3767
+ if (overrides) {
3768
+ for (const [key, value] of Object.entries(overrides)) {
3769
+ state[key] = value;
3770
+ }
3771
+ }
3772
+ return state;
3773
+ }
3774
+ function resolveAssumptions(config, profileName) {
3775
+ const profile = config.profiles?.[profileName];
3776
+ return profile?.parameters ?? {};
3777
+ }
3778
+ function classifyViability(state, world) {
3779
+ const gates = world.gates?.viability_classification ?? [];
3780
+ for (const gate of gates) {
3781
+ const fieldValue = state[gate.field];
3782
+ if (typeof fieldValue !== "number") continue;
3783
+ if (evaluateOperator(fieldValue, gate.operator, gate.value)) {
3784
+ return gate.status;
3785
+ }
3786
+ }
3787
+ return "MODEL_COLLAPSES";
3788
+ }
3789
+ function renderSimulateText(result) {
3790
+ const lines = [];
3791
+ lines.push(`SIMULATION: ${result.worldName}`);
3792
+ lines.push(`Profile: ${result.profile}`);
3793
+ lines.push(`Steps: ${result.steps.length}`);
3794
+ lines.push("");
3795
+ lines.push("INITIAL STATE");
3796
+ for (const [key, value] of Object.entries(result.initialState)) {
3797
+ lines.push(` ${key}: ${value}`);
3798
+ }
3799
+ lines.push("");
3800
+ for (const step of result.steps) {
3801
+ lines.push(`STEP ${step.step}`);
3802
+ const fired = step.rulesEvaluated.filter((r) => r.triggered);
3803
+ const skipped = step.rulesEvaluated.filter((r) => !r.triggered && !r.excluded);
3804
+ const excluded = step.rulesEvaluated.filter((r) => r.excluded);
3805
+ if (fired.length === 0) {
3806
+ lines.push(" No rules fired (state unchanged)");
3807
+ } else {
3808
+ for (const rule of fired) {
3809
+ lines.push(` FIRED: ${rule.label}`);
3810
+ for (const effect of rule.effects) {
3811
+ const beforeStr = formatValue(effect.before);
3812
+ const afterStr = formatValue(effect.after);
3813
+ lines.push(` ${effect.target}: ${beforeStr} -> ${afterStr}`);
3814
+ }
3815
+ if (rule.collapsed) {
3816
+ lines.push(` COLLAPSE on ${rule.collapseField}`);
3817
+ }
3818
+ }
3819
+ }
3820
+ if (excluded.length > 0) {
3821
+ lines.push(` Excluded: ${excluded.map((r) => r.label).join(", ")}`);
3822
+ }
3823
+ lines.push(` Viability: ${step.viability}`);
3824
+ if (step.collapsed) {
3825
+ lines.push(" ** MODEL COLLAPSED **");
3826
+ }
3827
+ lines.push("");
3828
+ }
3829
+ lines.push("FINAL STATE");
3830
+ for (const [key, value] of Object.entries(result.finalState)) {
3831
+ const initial = result.initialState[key];
3832
+ const changed = initial !== value;
3833
+ const marker = changed ? " (changed)" : "";
3834
+ lines.push(` ${key}: ${formatValue(value)}${marker}`);
3835
+ }
3836
+ lines.push("");
3837
+ lines.push(`VIABILITY: ${result.finalViability}`);
3838
+ if (result.collapsed) {
3839
+ lines.push(`COLLAPSED at step ${result.collapseStep} (rule: ${result.collapseRule})`);
3840
+ }
3841
+ return lines.join("\n");
3842
+ }
3843
+ function formatValue(v) {
3844
+ if (typeof v === "number") {
3845
+ return Number.isInteger(v) ? String(v) : v.toFixed(2);
3846
+ }
3847
+ return String(v);
3848
+ }
3849
+
3850
+ // src/engine/improve-engine.ts
3851
+ function improveWorld(world) {
3852
+ const suggestions = [];
3853
+ const report = validateWorld(world);
3854
+ addValidationSuggestions(report.findings, suggestions);
3855
+ analyzeRuleBalance(world, suggestions);
3856
+ analyzeStateCoverage(world, suggestions);
3857
+ analyzeGateCoverage(world, suggestions);
3858
+ analyzeAssumptions(world, suggestions);
3859
+ analyzeSimulationDynamics(world, suggestions);
3860
+ analyzeCompleteness(world, suggestions);
3861
+ const seen = /* @__PURE__ */ new Set();
3862
+ const unique = suggestions.filter((s) => {
3863
+ if (seen.has(s.id)) return false;
3864
+ seen.add(s.id);
3865
+ return true;
3866
+ });
3867
+ const priorityOrder = {
3868
+ critical: 0,
3869
+ high: 1,
3870
+ medium: 2,
3871
+ low: 3
3872
+ };
3873
+ unique.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
3874
+ const score = computeHealthScore(world, unique);
3875
+ return {
3876
+ worldId: world.world.world_id,
3877
+ worldName: world.world.name,
3878
+ score,
3879
+ suggestions: unique,
3880
+ stats: {
3881
+ critical: unique.filter((s) => s.priority === "critical").length,
3882
+ high: unique.filter((s) => s.priority === "high").length,
3883
+ medium: unique.filter((s) => s.priority === "medium").length,
3884
+ low: unique.filter((s) => s.priority === "low").length
3885
+ }
3886
+ };
3887
+ }
3888
+ function addValidationSuggestions(findings, suggestions) {
3889
+ for (const f of findings) {
3890
+ if (f.severity === "info") continue;
3891
+ const priority = f.severity === "error" ? "critical" : "high";
3892
+ const category = f.severity === "error" ? "fix" : "structure";
3893
+ suggestions.push({
3894
+ id: `validate-${f.id}`,
3895
+ priority,
3896
+ category,
3897
+ title: f.message,
3898
+ description: f.message,
3899
+ action: f.suggestion ?? `Review ${f.affectedBlocks.join(", ")}`,
3900
+ affectedFiles: f.affectedBlocks
3901
+ });
3902
+ }
3903
+ }
3904
+ function analyzeRuleBalance(world, suggestions) {
3905
+ if (!world.rules || world.rules.length === 0) return;
3906
+ const structural = world.rules.filter((r) => r.severity === "structural");
3907
+ const degradation = world.rules.filter((r) => r.severity === "degradation");
3908
+ const advantage = world.rules.filter((r) => r.severity === "advantage");
3909
+ if (structural.length === 0) {
3910
+ suggestions.push({
3911
+ id: "no-structural-rules",
3912
+ priority: "high",
3913
+ category: "balance",
3914
+ title: "No structural rules",
3915
+ description: "All rules are degradation or advantage. Structural rules define the core mechanics that hold the world together.",
3916
+ action: "Add at least one structural-severity rule that enforces a fundamental world constraint.",
3917
+ affectedFiles: ["rules/"]
3918
+ });
3919
+ }
3920
+ if (advantage.length === 0 && world.rules.length >= 3) {
3921
+ suggestions.push({
3922
+ id: "no-advantage-rules",
3923
+ priority: "medium",
3924
+ category: "balance",
3925
+ title: "No advantage rules",
3926
+ description: "All rules are negative (structural or degradation). Adding advantage rules creates positive feedback loops.",
3927
+ action: 'Add a rule with severity "advantage" that rewards desirable state.',
3928
+ affectedFiles: ["rules/"]
3929
+ });
3930
+ }
3931
+ if (degradation.length === 0 && world.rules.length >= 3) {
3932
+ suggestions.push({
3933
+ id: "no-degradation-rules",
3934
+ priority: "medium",
3935
+ category: "balance",
3936
+ title: "No degradation rules",
3937
+ description: "No rules model gradual decline. Degradation rules create realistic tension.",
3938
+ action: 'Add a rule with severity "degradation" that models gradual state decline.',
3939
+ affectedFiles: ["rules/"]
3940
+ });
3941
+ }
3942
+ const missingTranslation = world.rules.filter(
3943
+ (r) => !r.causal_translation?.trigger_text && !r.causal_translation?.effect_text
3944
+ );
3945
+ if (missingTranslation.length > 0 && missingTranslation.length <= 5) {
3946
+ suggestions.push({
3947
+ id: "missing-causal-translations",
3948
+ priority: "low",
3949
+ category: "completeness",
3950
+ title: `${missingTranslation.length} rule(s) missing causal translations`,
3951
+ description: "Causal translations provide human-readable narratives for rules. They improve explain output.",
3952
+ action: `Add causal_translation to: ${missingTranslation.map((r) => r.id).join(", ")}`,
3953
+ affectedFiles: ["rules/"]
3954
+ });
3955
+ }
3956
+ }
3957
+ function analyzeStateCoverage(world, suggestions) {
3958
+ if (!world.stateSchema?.variables || !world.rules) return;
3959
+ const variables = Object.keys(world.stateSchema.variables);
3960
+ const ruleTargets = /* @__PURE__ */ new Set();
3961
+ const ruleTriggers = /* @__PURE__ */ new Set();
3962
+ for (const rule of world.rules) {
3963
+ for (const t of rule.triggers) {
3964
+ if (t.source === "state") ruleTriggers.add(t.field);
3965
+ }
3966
+ for (const e of rule.effects ?? []) {
3967
+ ruleTargets.add(e.target);
3968
+ }
3969
+ for (const ce of rule.effects_conditional ?? []) {
3970
+ for (const e of ce.effects) {
3971
+ ruleTargets.add(e.target);
3972
+ }
3973
+ }
3974
+ }
3975
+ const writeOnly = variables.filter((v) => ruleTargets.has(v) && !ruleTriggers.has(v));
3976
+ if (writeOnly.length > 0) {
3977
+ suggestions.push({
3978
+ id: "write-only-variables",
3979
+ priority: "medium",
3980
+ category: "structure",
3981
+ title: `${writeOnly.length} write-only variable(s)`,
3982
+ description: `These variables are modified by rules but never trigger any rule: ${writeOnly.join(", ")}. They may be dead-end state.`,
3983
+ action: "Add rules that trigger on these variables to create feedback loops.",
3984
+ affectedFiles: ["rules/", "state-schema.json"]
3985
+ });
3986
+ }
3987
+ const readOnly = variables.filter((v) => ruleTriggers.has(v) && !ruleTargets.has(v));
3988
+ if (readOnly.length > 0 && readOnly.length <= 3) {
3989
+ suggestions.push({
3990
+ id: "read-only-variables",
3991
+ priority: "low",
3992
+ category: "structure",
3993
+ title: `${readOnly.length} input-only variable(s)`,
3994
+ description: `These variables trigger rules but are never modified: ${readOnly.join(", ")}. They act as fixed inputs.`,
3995
+ action: "This is fine for configuration inputs. If they should evolve, add rules that target them.",
3996
+ affectedFiles: ["state-schema.json"]
3997
+ });
3998
+ }
3999
+ }
4000
+ function analyzeGateCoverage(world, suggestions) {
4001
+ const gates = world.gates?.viability_classification ?? [];
4002
+ if (gates.length === 0) return;
4003
+ const statuses = new Set(gates.map((g) => g.status));
4004
+ const standard = ["THRIVING", "STABLE", "COMPRESSED", "CRITICAL", "MODEL_COLLAPSES"];
4005
+ const missing = standard.filter((s) => !statuses.has(s));
4006
+ if (missing.length > 0 && missing.length <= 2) {
4007
+ suggestions.push({
4008
+ id: "incomplete-viability-gates",
4009
+ priority: "medium",
4010
+ category: "structure",
4011
+ title: `Missing viability level(s): ${missing.join(", ")}`,
4012
+ description: "A complete viability ladder gives finer-grained status classification.",
4013
+ action: `Add gates for: ${missing.join(", ")}`,
4014
+ affectedFiles: ["gates.json"]
4015
+ });
4016
+ }
4017
+ }
4018
+ function analyzeAssumptions(world, suggestions) {
4019
+ const profiles = Object.keys(world.assumptions?.profiles ?? {});
4020
+ if (profiles.length === 1) {
4021
+ suggestions.push({
4022
+ id: "single-assumption-profile",
4023
+ priority: "medium",
4024
+ category: "completeness",
4025
+ title: "Only one assumption profile",
4026
+ description: "A single profile means no scenario comparison. Add an alternative profile to enable what-if analysis.",
4027
+ action: "Add a second profile with different parameter values to assumptions.json.",
4028
+ affectedFiles: ["assumptions.json"]
4029
+ });
4030
+ }
4031
+ }
4032
+ function analyzeSimulationDynamics(world, suggestions) {
4033
+ if (!world.rules || world.rules.length === 0) return;
4034
+ if (!world.stateSchema?.variables || Object.keys(world.stateSchema.variables).length === 0) return;
4035
+ try {
4036
+ const result = simulateWorld(world, { steps: 1 });
4037
+ const step = result.steps[0];
4038
+ if (step && step.rulesFired === 0) {
4039
+ suggestions.push({
4040
+ id: "no-rules-fire-default",
4041
+ priority: "high",
4042
+ category: "balance",
4043
+ title: "No rules fire with default state",
4044
+ description: "With all variables at their defaults, zero rules trigger. The world is inert until state changes.",
4045
+ action: "Adjust rule thresholds or state defaults so at least some rules fire in the baseline scenario.",
4046
+ affectedFiles: ["rules/", "state-schema.json"]
4047
+ });
4048
+ }
4049
+ if (result.collapsed && result.collapseStep === 1) {
4050
+ suggestions.push({
4051
+ id: "immediate-collapse",
4052
+ priority: "critical",
4053
+ category: "balance",
4054
+ title: "World collapses on first step",
4055
+ description: `Rule "${result.collapseRule}" triggers collapse immediately with default state. The world cannot sustain itself.`,
4056
+ action: "Adjust collapse thresholds or state defaults to prevent immediate collapse.",
4057
+ affectedFiles: ["rules/", "state-schema.json"]
4058
+ });
4059
+ }
4060
+ if (!result.collapsed) {
4061
+ const multiStep = simulateWorld(world, { steps: 5 });
4062
+ if (multiStep.collapsed) {
4063
+ suggestions.push({
4064
+ id: "eventual-collapse",
4065
+ priority: "high",
4066
+ category: "balance",
4067
+ title: `World collapses by step ${multiStep.collapseStep}`,
4068
+ description: `Starting from defaults, the world collapses after ${multiStep.collapseStep} steps. Consider adding stabilizing advantage rules.`,
4069
+ action: "Add advantage rules or adjust degradation rates to allow sustainable states.",
4070
+ affectedFiles: ["rules/"]
4071
+ });
4072
+ }
4073
+ }
4074
+ } catch {
4075
+ }
4076
+ }
4077
+ function analyzeCompleteness(world, suggestions) {
4078
+ if (!world.guards) {
4079
+ suggestions.push({
4080
+ id: "add-guards",
4081
+ priority: "low",
4082
+ category: "completeness",
4083
+ title: "No guards defined",
4084
+ description: "Guards enable runtime enforcement in Action Space. Without them, the world has no runtime protection.",
4085
+ action: "Add guards.json with structural guards backing each invariant.",
4086
+ affectedFiles: ["guards.json"]
4087
+ });
4088
+ }
4089
+ if (!world.roles) {
4090
+ suggestions.push({
4091
+ id: "add-roles",
4092
+ priority: "low",
4093
+ category: "completeness",
4094
+ title: "No roles defined",
4095
+ description: "Roles enable multi-agent governance with different permission levels.",
4096
+ action: "Add roles.json with at least an observer and steward role.",
4097
+ affectedFiles: ["roles.json"]
4098
+ });
4099
+ }
4100
+ if (!world.kernel) {
4101
+ suggestions.push({
4102
+ id: "add-kernel",
4103
+ priority: "low",
4104
+ category: "completeness",
4105
+ title: "No kernel configuration",
4106
+ description: "A kernel config provides Thinking Space governance with forbidden patterns and response vocabulary.",
4107
+ action: "Add kernel.json with input/output boundaries.",
4108
+ affectedFiles: ["kernel.json"]
4109
+ });
4110
+ }
4111
+ const outcomes = world.outcomes?.computed_outcomes ?? [];
4112
+ if (outcomes.length > 0 && !outcomes.some((o) => o.primary)) {
4113
+ suggestions.push({
4114
+ id: "no-primary-outcome",
4115
+ priority: "medium",
4116
+ category: "structure",
4117
+ title: "No primary outcome defined",
4118
+ description: "Marking one outcome as primary helps tools identify the main metric to display.",
4119
+ action: "Set primary: true on the most important computed outcome.",
4120
+ affectedFiles: ["outcomes.json"]
4121
+ });
4122
+ }
4123
+ }
4124
+ function computeHealthScore(world, suggestions) {
4125
+ let score = 100;
4126
+ for (const s of suggestions) {
4127
+ switch (s.priority) {
4128
+ case "critical":
4129
+ score -= 15;
4130
+ break;
4131
+ case "high":
4132
+ score -= 5;
4133
+ break;
4134
+ case "medium":
4135
+ score -= 2;
4136
+ break;
4137
+ case "low":
4138
+ score -= 1;
4139
+ break;
4140
+ }
4141
+ }
4142
+ return Math.max(0, Math.min(100, score));
4143
+ }
4144
+ function renderImproveText(report) {
4145
+ const lines = [];
4146
+ lines.push(`IMPROVE: ${report.worldName}`);
4147
+ lines.push(`Health Score: ${report.score}/100`);
4148
+ lines.push("");
4149
+ if (report.suggestions.length === 0) {
4150
+ lines.push("No suggestions \u2014 this world is in great shape.");
4151
+ return lines.join("\n");
4152
+ }
4153
+ const groups = [
4154
+ ["CRITICAL (must fix)", "critical", "x"],
4155
+ ["HIGH PRIORITY", "high", "!"],
4156
+ ["SUGGESTIONS", "medium", "-"],
4157
+ ["NICE TO HAVE", "low", "."]
4158
+ ];
4159
+ for (const [header, priority, icon] of groups) {
4160
+ const items = report.suggestions.filter((s) => s.priority === priority);
4161
+ if (items.length === 0) continue;
4162
+ lines.push(header);
4163
+ for (const s of items) {
4164
+ lines.push(` ${icon} ${s.title}`);
4165
+ lines.push(` Action: ${s.action}`);
4166
+ }
4167
+ lines.push("");
4168
+ }
4169
+ const { stats } = report;
4170
+ const parts = [];
4171
+ if (stats.critical > 0) parts.push(`${stats.critical} critical`);
4172
+ if (stats.high > 0) parts.push(`${stats.high} high`);
4173
+ if (stats.medium > 0) parts.push(`${stats.medium} medium`);
4174
+ if (stats.low > 0) parts.push(`${stats.low} low`);
4175
+ lines.push(`Total: ${report.suggestions.length} suggestions (${parts.join(", ")})`);
4176
+ return lines.join("\n");
4177
+ }
4178
+
4179
+ // src/contracts/derive-contract.ts
4180
+ var DERIVE_EXIT_CODES = {
4181
+ SUCCESS: 0,
4182
+ VALIDATION_FAIL: 1,
4183
+ INPUT_ERROR: 2,
4184
+ PROVIDER_ERROR: 3
4185
+ };
4186
+ var CONFIGURE_AI_EXIT_CODES = {
4187
+ SUCCESS: 0,
4188
+ VALIDATION_FAIL: 1,
4189
+ ERROR: 3
4190
+ };
4191
+ // Annotate the CommonJS export names for ESM import in node:
4192
+ 0 && (module.exports = {
4193
+ BOOTSTRAP_EXIT_CODES,
4194
+ CONFIGURE_AI_EXIT_CODES,
4195
+ CompositeAuditLogger,
4196
+ ConsoleAuditLogger,
4197
+ DERIVE_EXIT_CODES,
4198
+ FileAuditLogger,
4199
+ GUARD_EXIT_CODES,
4200
+ VALIDATE_EXIT_CODES,
4201
+ createGovernanceEngine,
4202
+ deriveWorld,
4203
+ emitWorldDefinition,
4204
+ evaluateCondition,
4205
+ evaluateGuard,
4206
+ eventToAllowlistKey,
4207
+ explainWorld,
4208
+ extractWorldMarkdown,
4209
+ formatVerdict,
4210
+ formatVerdictOneLine,
4211
+ generateImpactReport,
4212
+ generateImpactReportFromFile,
4213
+ improveWorld,
4214
+ loadWorld,
4215
+ loadWorldFromDirectory,
4216
+ normalizeWorldMarkdown,
4217
+ parseWorldMarkdown,
4218
+ readAuditLog,
4219
+ renderExplainText,
4220
+ renderImpactReport,
4221
+ renderImproveText,
4222
+ renderSimulateText,
4223
+ simulateWorld,
4224
+ summarizeAuditEvents,
4225
+ validateWorld,
4226
+ verdictToAuditEvent
4227
+ });