@neuroverseos/governance 0.3.0 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.well-known/ai-plugin.json +34 -9
- package/AGENTS.md +72 -24
- package/README.md +352 -237
- package/dist/adapters/autoresearch.cjs +1152 -3
- package/dist/adapters/autoresearch.d.cts +11 -3
- package/dist/adapters/autoresearch.d.ts +11 -3
- package/dist/adapters/autoresearch.js +9 -4
- package/dist/adapters/deep-agents.cjs +1528 -0
- package/dist/adapters/deep-agents.d.cts +181 -0
- package/dist/adapters/deep-agents.d.ts +181 -0
- package/dist/adapters/deep-agents.js +17 -0
- package/dist/adapters/express.cjs +171 -32
- package/dist/adapters/express.d.cts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +5 -5
- package/dist/adapters/index.cjs +564 -121
- package/dist/adapters/index.d.cts +3 -1
- package/dist/adapters/index.d.ts +3 -1
- package/dist/adapters/index.js +38 -16
- package/dist/adapters/langchain.cjs +217 -57
- package/dist/adapters/langchain.d.cts +5 -5
- package/dist/adapters/langchain.d.ts +5 -5
- package/dist/adapters/langchain.js +6 -5
- package/dist/adapters/openai.cjs +219 -59
- package/dist/adapters/openai.d.cts +5 -5
- package/dist/adapters/openai.d.ts +5 -5
- package/dist/adapters/openai.js +6 -5
- package/dist/adapters/openclaw.cjs +217 -57
- package/dist/adapters/openclaw.d.cts +6 -6
- package/dist/adapters/openclaw.d.ts +6 -6
- package/dist/adapters/openclaw.js +6 -5
- package/dist/add-ROOZLU62.js +314 -0
- package/dist/behavioral-MJO34S6Q.js +118 -0
- package/dist/{bootstrap-GXVDZNF7.js → bootstrap-CQRZVOXK.js} +6 -4
- package/dist/bootstrap-emitter-Q7UIJZ2O.js +7 -0
- package/dist/bootstrap-parser-EEF36XDU.js +7 -0
- package/dist/browser.global.js +941 -0
- package/dist/{build-P42YFKQV.js → build-QKOBBC23.js} +7 -5
- package/dist/{chunk-COT5XS4V.js → chunk-3WQLXYTP.js} +17 -35
- package/dist/{chunk-ER62HNGF.js → chunk-4FLICVVA.js} +17 -37
- package/dist/chunk-5TPFNWRU.js +215 -0
- package/dist/chunk-5U2MQO5P.js +57 -0
- package/dist/{chunk-NF5POFCI.js → chunk-6S5CFQXY.js} +6 -4
- package/dist/{chunk-QPASI2BR.js → chunk-A7GKPPU7.js} +49 -10
- package/dist/{chunk-OGL7QXZS.js → chunk-B6OXJLJ5.js} +17 -3
- package/dist/{chunk-2PQU3VAN.js → chunk-BNKJPUPQ.js} +17 -35
- package/dist/chunk-BQZMOEML.js +43 -0
- package/dist/chunk-CNSO6XW5.js +207 -0
- package/dist/{chunk-JZPQGIKR.js → chunk-CTZHONLA.js} +65 -9
- package/dist/chunk-D2UCV5AK.js +326 -0
- package/dist/{chunk-XPDMYECO.js → chunk-EMQDLDAF.js} +1 -185
- package/dist/{chunk-GR6DGCZ2.js → chunk-F66BVUYB.js} +3 -3
- package/dist/{chunk-2NICNKOM.js → chunk-G7DJ6VOD.js} +5 -4
- package/dist/{chunk-4A7LISES.js → chunk-IS4WUH6Y.js} +45 -6
- package/dist/{chunk-MWDQ4MJB.js → chunk-MH7BT4VH.js} +5 -1
- package/dist/chunk-O5ABKEA7.js +304 -0
- package/dist/chunk-PVTQQS3Y.js +186 -0
- package/dist/{chunk-4QXB6PEO.js → chunk-QLPTHTVB.js} +37 -16
- package/dist/chunk-QWGCMQQD.js +16 -0
- package/dist/{chunk-T5EUJQE5.js → chunk-QXBFT7NI.js} +31 -2
- package/dist/{chunk-PDOZHZWL.js → chunk-TG6SEF24.js} +25 -4
- package/dist/chunk-U6U7EJZL.js +177 -0
- package/dist/{chunk-4JRYGIO7.js → chunk-W7LLXRGY.js} +110 -7
- package/dist/{chunk-BUWWN2NX.js → chunk-ZJTDUCC2.js} +9 -7
- package/dist/{chunk-FYS2CBUW.js → chunk-ZWI3NIXK.js} +10 -0
- package/dist/cli/neuroverse.cjs +5091 -2348
- package/dist/cli/neuroverse.js +52 -21
- package/dist/cli/plan.cjs +881 -41
- package/dist/cli/plan.js +7 -15
- package/dist/cli/run.cjs +289 -34
- package/dist/cli/run.js +4 -4
- package/dist/{configure-ai-TK67ZWZL.js → configure-ai-6TZ3MCSI.js} +1 -1
- package/dist/decision-flow-M63D47LO.js +61 -0
- package/dist/demo-G43RLCPK.js +469 -0
- package/dist/{derive-TLIV4OOU.js → derive-FJZVIPUZ.js} +5 -4
- package/dist/{doctor-XPDLEYXN.js → doctor-6BC6X2VO.js} +6 -4
- package/dist/equity-penalties-SG5IZQ7I.js +244 -0
- package/dist/{explain-IDCRWMPX.js → explain-RHBU2GBR.js} +6 -25
- package/dist/{guard-RV65TT4L.js → guard-AJCCGZMF.js} +8 -12
- package/dist/{guard-contract-WZx__PmU.d.cts → guard-contract-DqFcTScd.d.cts} +117 -5
- package/dist/{guard-contract-WZx__PmU.d.ts → guard-contract-DqFcTScd.d.ts} +117 -5
- package/dist/{guard-engine-JLTUARGU.js → guard-engine-PNR6MHCM.js} +3 -3
- package/dist/{impact-XPECYRLH.js → impact-3XVDSCBU.js} +5 -5
- package/dist/{improve-GPUBKTEA.js → improve-TQP4ECSY.js} +7 -26
- package/dist/index.cjs +5597 -4279
- package/dist/index.d.cts +597 -18
- package/dist/index.d.ts +597 -18
- package/dist/index.js +134 -41
- package/dist/{infer-world-7GVZWFX4.js → infer-world-IFXCACJ5.js} +1 -1
- package/dist/{init-PKPIYHYE.js → init-FYPV4SST.js} +1 -1
- package/dist/{init-world-VWMQZQC7.js → init-world-TI7ARHBT.js} +1 -1
- package/dist/mcp-server-5Y3ZM7TV.js +13 -0
- package/dist/{model-adapter-BB7G4MFI.js → model-adapter-VXEKB4LS.js} +1 -1
- package/dist/{playground-E664U4T6.js → playground-VZBNPPBO.js} +29 -19
- package/dist/{redteam-Z7WREJ44.js → redteam-MZPZD3EF.js} +4 -4
- package/dist/session-JYOARW54.js +15 -0
- package/dist/shared-7RLUHNMU.js +16 -0
- package/dist/shared-B8dvUUD8.d.cts +60 -0
- package/dist/shared-Dr5Wiay8.d.ts +60 -0
- package/dist/{simulate-VDOYQFRO.js → simulate-LJXYBC6M.js} +8 -33
- package/dist/{test-OGXJK4QU.js → test-BOOR4A5F.js} +4 -4
- package/dist/{trace-JVF67VR3.js → trace-PKV4KX56.js} +4 -4
- package/dist/{validate-LLBWVPGV.js → validate-RALX7CZS.js} +2 -2
- package/dist/{validate-engine-UIABSIHD.js → validate-engine-7ZXFVGF2.js} +1 -1
- package/dist/viz/assets/index-B8SaeJZZ.js +23 -0
- package/dist/viz/index.html +23 -0
- package/dist/{world-LAXO6DOX.js → world-BIP4GZBZ.js} +9 -11
- package/dist/world-loader-Y6HMQH2D.js +13 -0
- package/dist/worlds/coding-agent.nv-world.md +211 -0
- package/dist/worlds/research-agent.nv-world.md +169 -0
- package/dist/worlds/social-media.nv-world.md +198 -0
- package/dist/worlds/trading-agent.nv-world.md +218 -0
- package/examples/social-media-sim/bridge.py +209 -0
- package/examples/social-media-sim/simulation.py +927 -0
- package/package.json +30 -4
- package/policies/content-moderation-rules.txt +8 -0
- package/policies/marketing-rules.txt +8 -0
- package/policies/science-research-rules.txt +11 -0
- package/policies/social-media-rules.txt +7 -0
- package/policies/strict-rules.txt +8 -0
- package/policies/trading-rules.txt +8 -0
- package/simulate.html +1567 -0
- package/dist/chunk-YZFATT7X.js +0 -9
- package/dist/mcp-server-FPVSU32Z.js +0 -13
- package/dist/session-EKTRSR7C.js +0 -14
- package/dist/world-loader-HMPTOEA2.js +0 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readAuditLog
|
|
3
|
-
} from "./chunk-
|
|
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
|
|
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:
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = [];
|