@neuroverseos/governance 0.3.0 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/.well-known/ai-plugin.json +34 -9
  2. package/AGENTS.md +72 -24
  3. package/README.md +352 -237
  4. package/dist/adapters/autoresearch.cjs +1152 -3
  5. package/dist/adapters/autoresearch.d.cts +11 -3
  6. package/dist/adapters/autoresearch.d.ts +11 -3
  7. package/dist/adapters/autoresearch.js +9 -4
  8. package/dist/adapters/deep-agents.cjs +1528 -0
  9. package/dist/adapters/deep-agents.d.cts +181 -0
  10. package/dist/adapters/deep-agents.d.ts +181 -0
  11. package/dist/adapters/deep-agents.js +17 -0
  12. package/dist/adapters/express.cjs +171 -32
  13. package/dist/adapters/express.d.cts +1 -1
  14. package/dist/adapters/express.d.ts +1 -1
  15. package/dist/adapters/express.js +5 -5
  16. package/dist/adapters/index.cjs +564 -121
  17. package/dist/adapters/index.d.cts +3 -1
  18. package/dist/adapters/index.d.ts +3 -1
  19. package/dist/adapters/index.js +38 -16
  20. package/dist/adapters/langchain.cjs +217 -57
  21. package/dist/adapters/langchain.d.cts +5 -5
  22. package/dist/adapters/langchain.d.ts +5 -5
  23. package/dist/adapters/langchain.js +6 -5
  24. package/dist/adapters/openai.cjs +219 -59
  25. package/dist/adapters/openai.d.cts +5 -5
  26. package/dist/adapters/openai.d.ts +5 -5
  27. package/dist/adapters/openai.js +6 -5
  28. package/dist/adapters/openclaw.cjs +217 -57
  29. package/dist/adapters/openclaw.d.cts +6 -6
  30. package/dist/adapters/openclaw.d.ts +6 -6
  31. package/dist/adapters/openclaw.js +6 -5
  32. package/dist/add-ROOZLU62.js +314 -0
  33. package/dist/behavioral-MJO34S6Q.js +118 -0
  34. package/dist/{bootstrap-GXVDZNF7.js → bootstrap-CQRZVOXK.js} +6 -4
  35. package/dist/bootstrap-emitter-Q7UIJZ2O.js +7 -0
  36. package/dist/bootstrap-parser-EEF36XDU.js +7 -0
  37. package/dist/browser.global.js +941 -0
  38. package/dist/{build-P42YFKQV.js → build-QKOBBC23.js} +7 -5
  39. package/dist/{chunk-COT5XS4V.js → chunk-3WQLXYTP.js} +17 -35
  40. package/dist/{chunk-ER62HNGF.js → chunk-4FLICVVA.js} +17 -37
  41. package/dist/chunk-5TPFNWRU.js +215 -0
  42. package/dist/chunk-5U2MQO5P.js +57 -0
  43. package/dist/{chunk-NF5POFCI.js → chunk-6S5CFQXY.js} +6 -4
  44. package/dist/{chunk-QPASI2BR.js → chunk-A7GKPPU7.js} +49 -10
  45. package/dist/{chunk-OGL7QXZS.js → chunk-B6OXJLJ5.js} +17 -3
  46. package/dist/{chunk-2PQU3VAN.js → chunk-BNKJPUPQ.js} +17 -35
  47. package/dist/chunk-BQZMOEML.js +43 -0
  48. package/dist/chunk-CNSO6XW5.js +207 -0
  49. package/dist/{chunk-JZPQGIKR.js → chunk-CTZHONLA.js} +65 -9
  50. package/dist/chunk-D2UCV5AK.js +326 -0
  51. package/dist/{chunk-XPDMYECO.js → chunk-EMQDLDAF.js} +1 -185
  52. package/dist/{chunk-GR6DGCZ2.js → chunk-F66BVUYB.js} +3 -3
  53. package/dist/{chunk-2NICNKOM.js → chunk-G7DJ6VOD.js} +5 -4
  54. package/dist/{chunk-4A7LISES.js → chunk-IS4WUH6Y.js} +45 -6
  55. package/dist/{chunk-MWDQ4MJB.js → chunk-MH7BT4VH.js} +5 -1
  56. package/dist/chunk-O5ABKEA7.js +304 -0
  57. package/dist/chunk-PVTQQS3Y.js +186 -0
  58. package/dist/{chunk-4QXB6PEO.js → chunk-QLPTHTVB.js} +37 -16
  59. package/dist/chunk-QWGCMQQD.js +16 -0
  60. package/dist/{chunk-T5EUJQE5.js → chunk-QXBFT7NI.js} +31 -2
  61. package/dist/{chunk-PDOZHZWL.js → chunk-TG6SEF24.js} +25 -4
  62. package/dist/chunk-U6U7EJZL.js +177 -0
  63. package/dist/{chunk-4JRYGIO7.js → chunk-W7LLXRGY.js} +110 -7
  64. package/dist/{chunk-BUWWN2NX.js → chunk-ZJTDUCC2.js} +9 -7
  65. package/dist/{chunk-FYS2CBUW.js → chunk-ZWI3NIXK.js} +10 -0
  66. package/dist/cli/neuroverse.cjs +5091 -2348
  67. package/dist/cli/neuroverse.js +52 -21
  68. package/dist/cli/plan.cjs +881 -41
  69. package/dist/cli/plan.js +7 -15
  70. package/dist/cli/run.cjs +289 -34
  71. package/dist/cli/run.js +4 -4
  72. package/dist/{configure-ai-TK67ZWZL.js → configure-ai-6TZ3MCSI.js} +1 -1
  73. package/dist/decision-flow-M63D47LO.js +61 -0
  74. package/dist/demo-G43RLCPK.js +469 -0
  75. package/dist/{derive-TLIV4OOU.js → derive-FJZVIPUZ.js} +5 -4
  76. package/dist/{doctor-XPDLEYXN.js → doctor-6BC6X2VO.js} +6 -4
  77. package/dist/equity-penalties-SG5IZQ7I.js +244 -0
  78. package/dist/{explain-IDCRWMPX.js → explain-RHBU2GBR.js} +6 -25
  79. package/dist/{guard-RV65TT4L.js → guard-AJCCGZMF.js} +8 -12
  80. package/dist/{guard-contract-WZx__PmU.d.cts → guard-contract-DqFcTScd.d.cts} +117 -5
  81. package/dist/{guard-contract-WZx__PmU.d.ts → guard-contract-DqFcTScd.d.ts} +117 -5
  82. package/dist/{guard-engine-JLTUARGU.js → guard-engine-PNR6MHCM.js} +3 -3
  83. package/dist/{impact-XPECYRLH.js → impact-3XVDSCBU.js} +5 -5
  84. package/dist/{improve-GPUBKTEA.js → improve-TQP4ECSY.js} +7 -26
  85. package/dist/index.cjs +5597 -4279
  86. package/dist/index.d.cts +597 -18
  87. package/dist/index.d.ts +597 -18
  88. package/dist/index.js +134 -41
  89. package/dist/{infer-world-7GVZWFX4.js → infer-world-IFXCACJ5.js} +1 -1
  90. package/dist/{init-PKPIYHYE.js → init-FYPV4SST.js} +1 -1
  91. package/dist/{init-world-VWMQZQC7.js → init-world-TI7ARHBT.js} +1 -1
  92. package/dist/mcp-server-5Y3ZM7TV.js +13 -0
  93. package/dist/{model-adapter-BB7G4MFI.js → model-adapter-VXEKB4LS.js} +1 -1
  94. package/dist/{playground-E664U4T6.js → playground-VZBNPPBO.js} +29 -19
  95. package/dist/{redteam-Z7WREJ44.js → redteam-MZPZD3EF.js} +4 -4
  96. package/dist/session-JYOARW54.js +15 -0
  97. package/dist/shared-7RLUHNMU.js +16 -0
  98. package/dist/shared-B8dvUUD8.d.cts +60 -0
  99. package/dist/shared-Dr5Wiay8.d.ts +60 -0
  100. package/dist/{simulate-VDOYQFRO.js → simulate-LJXYBC6M.js} +8 -33
  101. package/dist/{test-OGXJK4QU.js → test-BOOR4A5F.js} +4 -4
  102. package/dist/{trace-JVF67VR3.js → trace-PKV4KX56.js} +4 -4
  103. package/dist/{validate-LLBWVPGV.js → validate-RALX7CZS.js} +2 -2
  104. package/dist/{validate-engine-UIABSIHD.js → validate-engine-7ZXFVGF2.js} +1 -1
  105. package/dist/viz/assets/index-B8SaeJZZ.js +23 -0
  106. package/dist/viz/index.html +23 -0
  107. package/dist/{world-LAXO6DOX.js → world-BIP4GZBZ.js} +9 -11
  108. package/dist/world-loader-Y6HMQH2D.js +13 -0
  109. package/dist/worlds/coding-agent.nv-world.md +211 -0
  110. package/dist/worlds/research-agent.nv-world.md +169 -0
  111. package/dist/worlds/social-media.nv-world.md +198 -0
  112. package/dist/worlds/trading-agent.nv-world.md +218 -0
  113. package/examples/social-media-sim/bridge.py +209 -0
  114. package/examples/social-media-sim/simulation.py +927 -0
  115. package/package.json +30 -4
  116. package/policies/content-moderation-rules.txt +8 -0
  117. package/policies/marketing-rules.txt +8 -0
  118. package/policies/science-research-rules.txt +11 -0
  119. package/policies/social-media-rules.txt +7 -0
  120. package/policies/strict-rules.txt +8 -0
  121. package/policies/trading-rules.txt +8 -0
  122. package/simulate.html +1567 -0
  123. package/dist/chunk-YZFATT7X.js +0 -9
  124. package/dist/mcp-server-FPVSU32Z.js +0 -13
  125. package/dist/session-EKTRSR7C.js +0 -14
  126. package/dist/world-loader-HMPTOEA2.js +0 -9
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readAuditLog
3
- } from "./chunk-QPASI2BR.js";
3
+ } from "./chunk-A7GKPPU7.js";
4
4
 
5
5
  // src/engine/impact-report.ts
6
6
  function generateImpactReport(events) {
@@ -9,7 +9,11 @@ function generateImpactReport(events) {
9
9
  }
10
10
  const blocked = events.filter((e) => e.decision === "BLOCK");
11
11
  const paused = events.filter((e) => e.decision === "PAUSE");
12
- const prevented = [...blocked, ...paused];
12
+ const modified = events.filter((e) => e.decision === "MODIFY");
13
+ const penalized = events.filter((e) => e.decision === "PENALIZE");
14
+ const rewarded = events.filter((e) => e.decision === "REWARD");
15
+ const neutralEvents = events.filter((e) => e.decision === "NEUTRAL");
16
+ const prevented = [...blocked, ...paused, ...modified, ...penalized];
13
17
  const categoryMap = /* @__PURE__ */ new Map();
14
18
  for (const e of prevented) {
15
19
  const cat = classifyPreventionCategory(e);
@@ -94,6 +98,8 @@ function generateImpactReport(events) {
94
98
  violationMap.set(key, entry);
95
99
  }
96
100
  const repeatViolations = [...violationMap.values()].filter((v) => v.attempts > 1).sort((a, b) => b.attempts - a.attempts).slice(0, 10);
101
+ const allowedCount = events.filter((e) => e.decision === "ALLOW").length;
102
+ const redirected = events.length - allowedCount - neutralEvents.length;
97
103
  return {
98
104
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
99
105
  periodStart: events[0].timestamp,
@@ -102,8 +108,13 @@ function generateImpactReport(events) {
102
108
  totalEvaluations: events.length,
103
109
  totalBlocked: blocked.length,
104
110
  totalPaused: paused.length,
105
- totalAllowed: events.length - blocked.length - paused.length,
106
- preventionRate: prevented.length / events.length,
111
+ totalAllowed: allowedCount,
112
+ totalModified: modified.length,
113
+ totalPenalized: penalized.length,
114
+ totalRewarded: rewarded.length,
115
+ totalNeutral: neutralEvents.length,
116
+ preventionRate: events.length > 0 ? prevented.length / events.length : 0,
117
+ redirectionRate: events.length > 0 ? redirected / events.length : 0,
107
118
  preventedByCategory,
108
119
  topPreventedIntents,
109
120
  hotActors,
@@ -142,8 +153,13 @@ function renderImpactReport(report) {
142
153
  lines.push(` Total evaluations: ${report.totalEvaluations}`);
143
154
  lines.push(` Allowed: ${report.totalAllowed}`);
144
155
  lines.push(` Blocked: ${report.totalBlocked}`);
156
+ lines.push(` Modified: ${report.totalModified}`);
145
157
  lines.push(` Paused: ${report.totalPaused}`);
158
+ lines.push(` Penalized: ${report.totalPenalized}`);
159
+ lines.push(` Rewarded: ${report.totalRewarded}`);
160
+ lines.push(` Neutral: ${report.totalNeutral}`);
146
161
  lines.push(` Prevention rate: ${(report.preventionRate * 100).toFixed(1)}%`);
162
+ lines.push(` Redirection rate: ${(report.redirectionRate * 100).toFixed(1)}%`);
147
163
  lines.push("");
148
164
  if (report.totalBlocked > 0 || report.totalPaused > 0) {
149
165
  lines.push("WITHOUT GOVERNANCE");
@@ -208,7 +224,12 @@ function emptyReport() {
208
224
  totalBlocked: 0,
209
225
  totalPaused: 0,
210
226
  totalAllowed: 0,
227
+ totalModified: 0,
228
+ totalPenalized: 0,
229
+ totalRewarded: 0,
230
+ totalNeutral: 0,
211
231
  preventionRate: 0,
232
+ redirectionRate: 0,
212
233
  preventedByCategory: [],
213
234
  topPreventedIntents: [],
214
235
  hotActors: [],
@@ -0,0 +1,177 @@
1
+ import {
2
+ evaluateGuard
3
+ } from "./chunk-W7LLXRGY.js";
4
+ import {
5
+ loadWorld
6
+ } from "./chunk-CTZHONLA.js";
7
+
8
+ // src/runtime/govern.ts
9
+ function actionToGuardEvent(action) {
10
+ return {
11
+ intent: action.description,
12
+ tool: action.type,
13
+ roleId: action.agentId,
14
+ riskLevel: magnitudeToRisk(action.magnitude),
15
+ args: action.context
16
+ };
17
+ }
18
+ function magnitudeToRisk(magnitude) {
19
+ if (magnitude === void 0) return void 0;
20
+ if (magnitude < 0.25) return "low";
21
+ if (magnitude < 0.5) return "medium";
22
+ if (magnitude < 0.75) return "high";
23
+ return "critical";
24
+ }
25
+ function govern(action, world, options) {
26
+ const event = actionToGuardEvent(action);
27
+ return evaluateGuard(event, world, options);
28
+ }
29
+ async function createGovernor(config) {
30
+ const worldPath = config.worldPath;
31
+ if (!worldPath) {
32
+ throw new Error("Governor requires a worldPath");
33
+ }
34
+ const options = {
35
+ trace: config.trace,
36
+ level: config.level
37
+ };
38
+ let world = await loadWorld(worldPath);
39
+ return {
40
+ evaluate(action) {
41
+ return govern(action, world, options);
42
+ },
43
+ async reload() {
44
+ world = await loadWorld(worldPath);
45
+ },
46
+ get world() {
47
+ return world;
48
+ }
49
+ };
50
+ }
51
+ async function writeTempWorld(dir, policyLines) {
52
+ const { writeFile, mkdir } = await import("fs/promises");
53
+ const { join } = await import("path");
54
+ await mkdir(dir, { recursive: true });
55
+ const worldJson = {
56
+ world_id: "demo-live",
57
+ name: "Live Demo World",
58
+ thesis: "Interactive governance demo",
59
+ version: "0.1.0",
60
+ runtime_mode: "COMPLIANCE",
61
+ default_assumption_profile: "baseline",
62
+ default_alternative_profile: "baseline",
63
+ modules: [],
64
+ players: { thinking_space: false, experience_space: false, action_space: true }
65
+ };
66
+ const forbiddenPatterns = policyLines.filter((line) => line.trim().length > 0).map((line, i) => ({
67
+ id: `demo-rule-${i + 1}`,
68
+ pattern: line.trim(),
69
+ reason: line.trim(),
70
+ action: "BLOCK"
71
+ }));
72
+ const kernelJson = {
73
+ artifact_type: "kernel",
74
+ kernel_id: "demo-kernel",
75
+ version: "0.1.0",
76
+ domain: "demo",
77
+ enforcement_level: "standard",
78
+ input_boundaries: { forbidden_patterns: forbiddenPatterns },
79
+ output_boundaries: { forbidden_patterns: [] },
80
+ response_vocabulary: {},
81
+ metadata: {
82
+ compiled_by: "neuroverse-demo",
83
+ compiled_at: (/* @__PURE__ */ new Date()).toISOString(),
84
+ source_hash: "live-edit",
85
+ compiler_version: "0.2.2"
86
+ }
87
+ };
88
+ const metadataJson = {
89
+ format_version: "1.0.0",
90
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
91
+ last_modified: (/* @__PURE__ */ new Date()).toISOString(),
92
+ authoring_method: "manual-authoring"
93
+ };
94
+ await Promise.all([
95
+ writeFile(join(dir, "world.json"), JSON.stringify(worldJson, null, 2)),
96
+ writeFile(join(dir, "kernel.json"), JSON.stringify(kernelJson, null, 2)),
97
+ writeFile(join(dir, "metadata.json"), JSON.stringify(metadataJson, null, 2))
98
+ ]);
99
+ }
100
+
101
+ // src/engine/api.ts
102
+ function handleHealthCheck() {
103
+ return {
104
+ status: "ok",
105
+ engine: "@neuroverseos/governance",
106
+ version: "0.2.2",
107
+ capabilities: [
108
+ "guard",
109
+ "simulate",
110
+ "validate",
111
+ "bootstrap",
112
+ "decision-flow",
113
+ "impact-report",
114
+ "behavioral-analysis"
115
+ ]
116
+ };
117
+ }
118
+ async function handleListPresets(policiesDir) {
119
+ const { readdir, readFile } = await import("fs/promises");
120
+ const { join } = await import("path");
121
+ const dir = policiesDir ?? join(process.cwd(), "policies");
122
+ const presets = [];
123
+ try {
124
+ const files = await readdir(dir);
125
+ for (const file of files.filter((f) => f.endsWith(".txt")).sort()) {
126
+ const content = await readFile(join(dir, file), "utf-8");
127
+ const id = file.replace(".txt", "");
128
+ const name = id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
129
+ const firstLine = content.split("\n").find((l) => l.trim().length > 0) ?? "";
130
+ presets.push({ id, name, description: firstLine, rules: content });
131
+ }
132
+ } catch {
133
+ }
134
+ return { presets };
135
+ }
136
+ async function handleReasonRequest(body) {
137
+ const intent = body.intent ?? body.scenario;
138
+ if (!intent) {
139
+ return { status: "error", error: "intent or scenario is required" };
140
+ }
141
+ const event = {
142
+ intent,
143
+ tool: body.tool,
144
+ roleId: body.roleId
145
+ };
146
+ if (body.worldPath) {
147
+ try {
148
+ const world = await loadWorld(body.worldPath);
149
+ const verdict = evaluateGuard(event, world);
150
+ return { status: "ok", verdict };
151
+ } catch (err) {
152
+ return { status: "error", error: `Failed to load world: ${err}` };
153
+ }
154
+ }
155
+ return { status: "error", error: "worldPath is required" };
156
+ }
157
+ function handleCreateCapsule(body) {
158
+ const capsuleId = `cap_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
159
+ return {
160
+ capsuleId,
161
+ scenario: body.scenario ?? "Untitled scenario",
162
+ rules: body.rules ?? [],
163
+ events: body.events ?? [],
164
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
165
+ };
166
+ }
167
+
168
+ export {
169
+ actionToGuardEvent,
170
+ govern,
171
+ createGovernor,
172
+ writeTempWorld,
173
+ handleHealthCheck,
174
+ handleListPresets,
175
+ handleReasonRequest,
176
+ handleCreateCapsule
177
+ };
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  buildPlanCheck,
3
- evaluatePlan
4
- } from "./chunk-4QXB6PEO.js";
3
+ evaluatePlan,
4
+ matchesAllKeywords,
5
+ normalizeEventText
6
+ } from "./chunk-QLPTHTVB.js";
5
7
 
6
8
  // src/engine/guard-engine.ts
7
9
  var PROMPT_INJECTION_PATTERNS = [
@@ -87,11 +89,49 @@ function isExternalScope(scope) {
87
89
  ];
88
90
  return !internalPatterns.some((p) => p.test(scope));
89
91
  }
92
+ var MAX_INPUT_LENGTH = 1e5;
90
93
  function evaluateGuard(event, world, options = {}) {
91
94
  const startTime = performance.now();
92
95
  const level = options.level ?? "standard";
93
96
  const includeTrace = options.trace ?? false;
94
- const eventText = (event.intent + " " + (event.tool ?? "") + " " + (event.scope ?? "")).toLowerCase();
97
+ if (!event.intent || typeof event.intent !== "string") {
98
+ return {
99
+ status: "BLOCK",
100
+ reason: "GuardEvent.intent is required and must be a string",
101
+ ruleId: "safety-input-validation",
102
+ evidence: {
103
+ worldId: world.world?.world_id ?? "",
104
+ worldName: world.world?.name ?? "",
105
+ worldVersion: world.world?.version ?? "",
106
+ evaluatedAt: Date.now(),
107
+ invariantsSatisfied: 0,
108
+ invariantsTotal: 0,
109
+ guardsMatched: [],
110
+ rulesMatched: [],
111
+ enforcementLevel: level
112
+ }
113
+ };
114
+ }
115
+ const inputLength = event.intent.length + (event.tool?.length ?? 0) + (event.scope?.length ?? 0) + (event.payload ? JSON.stringify(event.payload).length : 0);
116
+ if (inputLength > MAX_INPUT_LENGTH) {
117
+ return {
118
+ status: "BLOCK",
119
+ reason: `Input exceeds maximum allowed length (${MAX_INPUT_LENGTH} characters)`,
120
+ ruleId: "safety-input-length",
121
+ evidence: {
122
+ worldId: world.world?.world_id ?? "",
123
+ worldName: world.world?.name ?? "",
124
+ worldVersion: world.world?.version ?? "",
125
+ evaluatedAt: Date.now(),
126
+ invariantsSatisfied: 0,
127
+ invariantsTotal: 0,
128
+ guardsMatched: [],
129
+ rulesMatched: [],
130
+ enforcementLevel: level
131
+ }
132
+ };
133
+ }
134
+ const eventText = normalizeEventText(event);
95
135
  const invariantChecks = [];
96
136
  const safetyChecks = [];
97
137
  let planCheckResult;
@@ -104,6 +144,43 @@ function evaluateGuard(event, world, options = {}) {
104
144
  const guardsMatched = [];
105
145
  const rulesMatched = [];
106
146
  checkInvariantCoverage(world, invariantChecks);
147
+ if (event.roleId && options.agentStates) {
148
+ const agentState = options.agentStates.get(event.roleId);
149
+ if (agentState && agentState.cooldownRemaining > 0) {
150
+ decidingLayer = "safety";
151
+ decidingId = `penalize-cooldown-${event.roleId}`;
152
+ const verdict = buildVerdict(
153
+ "PENALIZE",
154
+ `Agent "${event.roleId}" is frozen for ${agentState.cooldownRemaining} more round(s) due to prior penalty.`,
155
+ `penalize-cooldown-${event.roleId}`,
156
+ void 0,
157
+ world,
158
+ level,
159
+ invariantChecks,
160
+ guardsMatched,
161
+ rulesMatched,
162
+ includeTrace ? buildTrace(
163
+ invariantChecks,
164
+ safetyChecks,
165
+ planCheckResult,
166
+ roleChecks,
167
+ guardChecks,
168
+ kernelRuleChecks,
169
+ levelChecks,
170
+ decidingLayer,
171
+ decidingId,
172
+ startTime
173
+ ) : void 0
174
+ );
175
+ verdict.intentRecord = {
176
+ originalIntent: event.intent,
177
+ finalAction: "blocked (agent frozen)",
178
+ enforcement: "PENALIZE",
179
+ consequence: { type: "freeze", rounds: agentState.cooldownRemaining, description: "Agent still in cooldown from prior penalty" }
180
+ };
181
+ return verdict;
182
+ }
183
+ }
107
184
  if (options.sessionAllowlist) {
108
185
  const key = eventToAllowlistKey(event);
109
186
  if (options.sessionAllowlist.has(key)) {
@@ -231,7 +308,16 @@ function evaluateGuard(event, world, options = {}) {
231
308
  if (guardVerdict.status !== "ALLOW") {
232
309
  decidingLayer = "guard";
233
310
  decidingId = guardVerdict.ruleId;
234
- return buildVerdict(
311
+ const intentRecord = {
312
+ originalIntent: event.intent,
313
+ finalAction: guardVerdict.status === "MODIFY" ? guardVerdict.modifiedTo ?? "modified" : guardVerdict.status === "PENALIZE" ? "blocked + penalized" : guardVerdict.status === "REWARD" ? event.intent : guardVerdict.status === "NEUTRAL" ? event.intent : guardVerdict.status === "BLOCK" ? "blocked" : "paused",
314
+ ruleApplied: guardVerdict.ruleId,
315
+ enforcement: guardVerdict.status,
316
+ modifiedTo: guardVerdict.modifiedTo,
317
+ consequence: guardVerdict.consequence,
318
+ reward: guardVerdict.reward
319
+ };
320
+ const verdict = buildVerdict(
235
321
  guardVerdict.status,
236
322
  guardVerdict.reason,
237
323
  guardVerdict.ruleId,
@@ -254,6 +340,10 @@ function evaluateGuard(event, world, options = {}) {
254
340
  startTime
255
341
  ) : void 0
256
342
  );
343
+ verdict.intentRecord = intentRecord;
344
+ if (guardVerdict.consequence) verdict.consequence = guardVerdict.consequence;
345
+ if (guardVerdict.reward) verdict.reward = guardVerdict.reward;
346
+ return verdict;
257
347
  }
258
348
  }
259
349
  const kernelVerdict = checkKernelRules(eventText, world, kernelRuleChecks, rulesMatched);
@@ -548,6 +638,21 @@ function checkGuards(event, eventText, world, checks, guardsMatched) {
548
638
  if (actionMode === "pause") {
549
639
  return { status: "PAUSE", reason, ruleId: `guard-${guard.id}` };
550
640
  }
641
+ if (actionMode === "penalize") {
642
+ const consequence = guard.consequence ? { ...guard.consequence } : { type: "freeze", rounds: 1, description: `Penalized for violating: ${guard.label}` };
643
+ return { status: "PENALIZE", reason, ruleId: `guard-${guard.id}`, consequence };
644
+ }
645
+ if (actionMode === "reward") {
646
+ const reward = guard.reward ? { ...guard.reward } : { type: "boost_influence", magnitude: 0.1, description: `Rewarded for: ${guard.label}` };
647
+ return { status: "REWARD", reason, ruleId: `guard-${guard.id}`, reward };
648
+ }
649
+ if (actionMode === "modify") {
650
+ const modifiedTo = guard.modify_to ?? guard.redirect ?? "hold";
651
+ return { status: "MODIFY", reason: `${reason} \u2192 Modified to: ${modifiedTo}`, ruleId: `guard-${guard.id}`, modifiedTo };
652
+ }
653
+ if (actionMode === "neutral") {
654
+ return { status: "NEUTRAL", reason, ruleId: `guard-${guard.id}` };
655
+ }
551
656
  if (actionMode === "warn" && !warnResult) {
552
657
  warnResult = { status: "ALLOW", warning: reason, ruleId: `guard-${guard.id}` };
553
658
  }
@@ -657,9 +762,7 @@ function checkLevelConstraints(event, level, checks) {
657
762
  return null;
658
763
  }
659
764
  function matchesKeywords(eventText, ruleText) {
660
- const keywords = ruleText.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
661
- if (keywords.length === 0) return false;
662
- return keywords.every((kw) => eventText.includes(kw));
765
+ return matchesAllKeywords(eventText, ruleText);
663
766
  }
664
767
  function eventToAllowlistKey(event) {
665
768
  return `${(event.tool ?? "*").toLowerCase()}::${event.intent.toLowerCase().trim()}`;
@@ -1,3 +1,12 @@
1
+ // src/engine/rule-utils.ts
2
+ function collectAllEffects(rule) {
3
+ const effects = [...rule.effects ?? []];
4
+ for (const ce of rule.effects_conditional ?? []) {
5
+ effects.push(...ce.effects);
6
+ }
7
+ return effects;
8
+ }
9
+
1
10
  // src/engine/explain-engine.ts
2
11
  function detectTensions(rules) {
3
12
  const increases = /* @__PURE__ */ new Map();
@@ -31,13 +40,6 @@ function detectTensions(rules) {
31
40
  }
32
41
  return tensions;
33
42
  }
34
- function collectAllEffects(rule) {
35
- const effects = [...rule.effects ?? []];
36
- for (const ce of rule.effects_conditional ?? []) {
37
- effects.push(...ce.effects);
38
- }
39
- return effects;
40
- }
41
43
  function explainWorld(world) {
42
44
  const dynamics = [...world.rules].sort((a, b) => a.order - b.order).map((rule) => {
43
45
  const allEffects = collectAllEffects(rule);
@@ -1,8 +1,18 @@
1
1
  // src/engine/simulate-engine.ts
2
2
  function simulateWorld(world, options = {}) {
3
+ if (!world || !world.world) {
4
+ throw new Error(
5
+ 'World definition required. simulateWorld() cannot run without a world.\nLoad one with: loadWorld("./world/") or parseWorldMarkdown(markdown)'
6
+ );
7
+ }
3
8
  const steps = Math.max(1, Math.min(options.steps ?? 1, 50));
4
9
  const profileName = options.profile ?? world.world.default_assumption_profile;
5
10
  const state = buildInitialState(world.stateSchema, options.stateOverrides);
11
+ for (const outcome of world.outcomes?.computed_outcomes ?? []) {
12
+ if (!(outcome.id in state)) {
13
+ state[outcome.id] = outcome.primary ? 100 : 0;
14
+ }
15
+ }
6
16
  const assumptions = resolveAssumptions(world.assumptions, profileName);
7
17
  const initialState = { ...state };
8
18
  const simulationSteps = [];