@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.
- package/LICENSE.md +190 -0
- package/README.md +472 -115
- package/dist/adapters/express.cjs +874 -0
- package/dist/adapters/express.d.cts +66 -0
- package/dist/adapters/express.d.ts +66 -0
- package/dist/adapters/express.js +10 -0
- package/dist/adapters/index.cjs +1166 -0
- package/dist/adapters/index.d.cts +5 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.js +40 -0
- package/dist/adapters/langchain.cjs +861 -0
- package/dist/adapters/langchain.d.cts +82 -0
- package/dist/adapters/langchain.d.ts +82 -0
- package/dist/adapters/langchain.js +14 -0
- package/dist/adapters/openai.cjs +891 -0
- package/dist/adapters/openai.d.cts +92 -0
- package/dist/adapters/openai.d.ts +92 -0
- package/dist/adapters/openai.js +14 -0
- package/dist/adapters/openclaw.cjs +883 -0
- package/dist/adapters/openclaw.d.cts +92 -0
- package/dist/adapters/openclaw.d.ts +92 -0
- package/dist/adapters/openclaw.js +14 -0
- package/dist/bootstrap-H4HHKQ5G.js +113 -0
- package/dist/build-73KAVHEY.js +308 -0
- package/dist/chunk-4NGDRRQH.js +10 -0
- package/dist/chunk-B4NF3OLW.js +673 -0
- package/dist/chunk-BUWWN2NX.js +192 -0
- package/dist/chunk-CROPZ75A.js +107 -0
- package/dist/chunk-D7BGWV2J.js +620 -0
- package/dist/chunk-EIUHJXBB.js +340 -0
- package/dist/chunk-EQXFOKH2.js +85 -0
- package/dist/chunk-FYPYZFV5.js +187 -0
- package/dist/chunk-FYS2CBUW.js +304 -0
- package/dist/chunk-FZQCRGUU.js +115 -0
- package/dist/chunk-I3RRAYK2.js +11 -0
- package/dist/chunk-ITJ3LCPG.js +225 -0
- package/dist/chunk-M3TZFGHO.js +79 -0
- package/dist/chunk-MWDQ4MJB.js +11 -0
- package/dist/chunk-O5OMJMIE.js +590 -0
- package/dist/chunk-T4X42QXC.js +121 -0
- package/dist/chunk-XPDMYECO.js +642 -0
- package/dist/chunk-Z2S2HIV5.js +100 -0
- package/dist/cli/neuroverse.cjs +5864 -0
- package/dist/cli/neuroverse.d.cts +1 -0
- package/dist/cli/neuroverse.d.ts +1 -0
- package/dist/cli/neuroverse.js +128 -0
- package/dist/configure-ai-46JVG56I.js +129 -0
- package/dist/derive-6NAEWLM5.js +150 -0
- package/dist/explain-3B3VB6TL.js +69 -0
- package/dist/guard-67Y66P3I.js +82 -0
- package/dist/guard-contract-D_RQz9kt.d.cts +529 -0
- package/dist/guard-contract-D_RQz9kt.d.ts +529 -0
- package/dist/impact-CHERK3O6.js +57 -0
- package/dist/improve-YG6I6ERG.js +84 -0
- package/dist/index.cjs +4227 -0
- package/dist/index.d.cts +1147 -0
- package/dist/index.d.ts +1139 -25
- package/dist/index.js +318 -1159
- package/dist/init-Z66T6TDI.js +142 -0
- package/dist/simulate-ETHHINZ4.js +107 -0
- package/dist/trace-3YODSSIP.js +164 -0
- package/dist/validate-UVE6GKQU.js +72 -0
- package/dist/validate-engine-657D75OG.js +6 -0
- package/dist/world-WLNHL5XC.js +298 -0
- package/dist/worlds/derivation-world.nv-world.md +278 -0
- package/package.json +74 -26
- package/LICENSE +0 -21
- package/dist/audit-logger.d.ts +0 -38
- package/dist/audit-logger.d.ts.map +0 -1
- package/dist/audit-logger.js +0 -100
- package/dist/audit-logger.js.map +0 -1
- package/dist/condition-engine.d.ts +0 -16
- package/dist/condition-engine.d.ts.map +0 -1
- package/dist/condition-engine.js +0 -186
- package/dist/condition-engine.js.map +0 -1
- package/dist/drift-monitor.d.ts +0 -81
- package/dist/drift-monitor.d.ts.map +0 -1
- package/dist/drift-monitor.js +0 -228
- package/dist/drift-monitor.js.map +0 -1
- package/dist/governance-engine.d.ts +0 -131
- package/dist/governance-engine.d.ts.map +0 -1
- package/dist/governance-engine.js +0 -660
- package/dist/governance-engine.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/types.d.ts +0 -196
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -9
- package/dist/types.js.map +0 -1
- package/dist/world-bootstrap.d.ts +0 -31
- package/dist/world-bootstrap.d.ts.map +0 -1
- package/dist/world-bootstrap.js +0 -415
- package/dist/world-bootstrap.js.map +0 -1
- package/openclaw.plugin.json +0 -59
package/dist/index.js
CHANGED
|
@@ -1,1170 +1,329 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
process.stderr.write(`\n[governance] PAUSE\n`);
|
|
73
|
-
}
|
|
74
|
-
if (ctx.agentId) {
|
|
75
|
-
const roleSuffix = ctx.role ? ` (role: ${ctx.role})` : ' (unbound)';
|
|
76
|
-
process.stderr.write(` agent: ${ctx.agentId}${roleSuffix}\n`);
|
|
77
|
-
}
|
|
78
|
-
const actionTarget = ctx.scope ? ` → ${ctx.scope}` : '';
|
|
79
|
-
process.stderr.write(` action: ${ctx.tool}${actionTarget}\n`);
|
|
80
|
-
process.stderr.write(` guard: ${ctx.guardLabel}\n`);
|
|
81
|
-
if (ctx.guardDescription) {
|
|
82
|
-
process.stderr.write(` "${ctx.guardDescription}"\n`);
|
|
83
|
-
}
|
|
84
|
-
if (ctx.evidence) {
|
|
85
|
-
process.stderr.write(` evidence: ${ctx.evidence}\n`);
|
|
86
|
-
}
|
|
87
|
-
// Governance-level pauses get explicit denial guidance
|
|
88
|
-
if (isGovernance) {
|
|
89
|
-
process.stderr.write(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
|
|
90
|
-
process.stderr.write(` 🚫 DENY this if you did not request this change.\n`);
|
|
91
|
-
}
|
|
92
|
-
process.stderr.write(` Allow? [y]es once / [a]lways / [n]o: `);
|
|
93
|
-
rl.on('line', (answer) => {
|
|
94
|
-
rl.close();
|
|
95
|
-
const a = answer.trim().toLowerCase();
|
|
96
|
-
if (a === 'a' || a === 'always') {
|
|
97
|
-
resolvePrompt('allow-always');
|
|
98
|
-
}
|
|
99
|
-
else if (a === 'n' || a === 'no') {
|
|
100
|
-
resolvePrompt('deny');
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
resolvePrompt('allow-once');
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
rl.on('close', () => {
|
|
107
|
-
resolvePrompt('deny');
|
|
108
|
-
});
|
|
109
|
-
});
|
|
1
|
+
import {
|
|
2
|
+
generateImpactReport,
|
|
3
|
+
generateImpactReportFromFile,
|
|
4
|
+
renderImpactReport
|
|
5
|
+
} from "./chunk-ITJ3LCPG.js";
|
|
6
|
+
import {
|
|
7
|
+
CompositeAuditLogger,
|
|
8
|
+
ConsoleAuditLogger,
|
|
9
|
+
FileAuditLogger,
|
|
10
|
+
createGovernanceEngine,
|
|
11
|
+
readAuditLog,
|
|
12
|
+
summarizeAuditEvents,
|
|
13
|
+
verdictToAuditEvent
|
|
14
|
+
} from "./chunk-FYPYZFV5.js";
|
|
15
|
+
import {
|
|
16
|
+
deriveWorld,
|
|
17
|
+
extractWorldMarkdown,
|
|
18
|
+
normalizeWorldMarkdown
|
|
19
|
+
} from "./chunk-D7BGWV2J.js";
|
|
20
|
+
import {
|
|
21
|
+
CONFIGURE_AI_EXIT_CODES,
|
|
22
|
+
DERIVE_EXIT_CODES
|
|
23
|
+
} from "./chunk-T4X42QXC.js";
|
|
24
|
+
import {
|
|
25
|
+
explainWorld,
|
|
26
|
+
renderExplainText
|
|
27
|
+
} from "./chunk-BUWWN2NX.js";
|
|
28
|
+
import {
|
|
29
|
+
improveWorld,
|
|
30
|
+
renderImproveText
|
|
31
|
+
} from "./chunk-EIUHJXBB.js";
|
|
32
|
+
import {
|
|
33
|
+
renderSimulateText,
|
|
34
|
+
simulateWorld
|
|
35
|
+
} from "./chunk-FYS2CBUW.js";
|
|
36
|
+
import {
|
|
37
|
+
BOOTSTRAP_EXIT_CODES
|
|
38
|
+
} from "./chunk-4NGDRRQH.js";
|
|
39
|
+
import {
|
|
40
|
+
emitWorldDefinition,
|
|
41
|
+
parseWorldMarkdown
|
|
42
|
+
} from "./chunk-XPDMYECO.js";
|
|
43
|
+
import {
|
|
44
|
+
VALIDATE_EXIT_CODES
|
|
45
|
+
} from "./chunk-I3RRAYK2.js";
|
|
46
|
+
import {
|
|
47
|
+
GUARD_EXIT_CODES
|
|
48
|
+
} from "./chunk-MWDQ4MJB.js";
|
|
49
|
+
import {
|
|
50
|
+
evaluateGuard,
|
|
51
|
+
eventToAllowlistKey
|
|
52
|
+
} from "./chunk-B4NF3OLW.js";
|
|
53
|
+
import {
|
|
54
|
+
loadWorld,
|
|
55
|
+
loadWorldFromDirectory
|
|
56
|
+
} from "./chunk-M3TZFGHO.js";
|
|
57
|
+
import {
|
|
58
|
+
validateWorld
|
|
59
|
+
} from "./chunk-O5OMJMIE.js";
|
|
60
|
+
|
|
61
|
+
// src/engine/verdict-formatter.ts
|
|
62
|
+
var ANSI = {
|
|
63
|
+
red: "\x1B[31m",
|
|
64
|
+
green: "\x1B[32m",
|
|
65
|
+
yellow: "\x1B[33m",
|
|
66
|
+
bold: "\x1B[1m",
|
|
67
|
+
dim: "\x1B[2m",
|
|
68
|
+
reset: "\x1B[0m"
|
|
69
|
+
};
|
|
70
|
+
function c(text, ...codes) {
|
|
71
|
+
return codes.join("") + text + ANSI.reset;
|
|
110
72
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
73
|
+
function formatVerdict(verdict, options = {}) {
|
|
74
|
+
const {
|
|
75
|
+
showRuleId = true,
|
|
76
|
+
showEvidence = false,
|
|
77
|
+
showWarning = true,
|
|
78
|
+
color = false,
|
|
79
|
+
compact = false
|
|
80
|
+
} = options;
|
|
81
|
+
if (compact) {
|
|
82
|
+
return formatCompact(verdict, { showRuleId, showWarning, color });
|
|
83
|
+
}
|
|
84
|
+
return formatFull(verdict, { showRuleId, showEvidence, showWarning, color });
|
|
85
|
+
}
|
|
86
|
+
function formatCompact(verdict, opts) {
|
|
87
|
+
const status = formatStatus(verdict.status, opts.color);
|
|
88
|
+
const parts = [status];
|
|
89
|
+
if (opts.showRuleId && verdict.ruleId) {
|
|
90
|
+
parts.push(verdict.ruleId);
|
|
91
|
+
}
|
|
92
|
+
if (verdict.reason) {
|
|
93
|
+
parts.push(verdict.reason);
|
|
94
|
+
}
|
|
95
|
+
if (opts.showWarning && verdict.warning) {
|
|
96
|
+
parts.push(`warning: ${verdict.warning}`);
|
|
97
|
+
}
|
|
98
|
+
return parts.join(" \u2014 ");
|
|
99
|
+
}
|
|
100
|
+
function formatFull(verdict, opts) {
|
|
101
|
+
const lines = [];
|
|
102
|
+
lines.push(formatStatus(verdict.status, opts.color));
|
|
103
|
+
if (opts.showRuleId && verdict.ruleId) {
|
|
104
|
+
lines.push(` Rule: ${verdict.ruleId}`);
|
|
105
|
+
}
|
|
106
|
+
if (verdict.reason) {
|
|
107
|
+
lines.push(` Reason: ${verdict.reason}`);
|
|
108
|
+
}
|
|
109
|
+
if (opts.showWarning && verdict.warning) {
|
|
110
|
+
const label = opts.color ? c("Warning:", ANSI.yellow) : "Warning:";
|
|
111
|
+
lines.push(` ${label} ${verdict.warning}`);
|
|
112
|
+
}
|
|
113
|
+
if (opts.showEvidence) {
|
|
114
|
+
const ev = verdict.evidence;
|
|
115
|
+
lines.push(` World: ${ev.worldName} v${ev.worldVersion}`);
|
|
116
|
+
lines.push(` Invariants: ${ev.invariantsSatisfied}/${ev.invariantsTotal} covered`);
|
|
117
|
+
if (ev.guardsMatched.length > 0) {
|
|
118
|
+
lines.push(` Guards matched: ${ev.guardsMatched.join(", ")}`);
|
|
126
119
|
}
|
|
127
|
-
|
|
128
|
-
|
|
120
|
+
if (ev.rulesMatched.length > 0) {
|
|
121
|
+
lines.push(` Rules matched: ${ev.rulesMatched.join(", ")}`);
|
|
129
122
|
}
|
|
123
|
+
lines.push(` Enforcement: ${ev.enforcementLevel}`);
|
|
124
|
+
}
|
|
125
|
+
return lines.join("\n");
|
|
130
126
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (params.url && typeof params.url === 'string')
|
|
143
|
-
return params.url;
|
|
144
|
-
if (params.path && typeof params.path === 'string')
|
|
145
|
-
return params.path;
|
|
146
|
-
return undefined;
|
|
127
|
+
function formatStatus(status, color) {
|
|
128
|
+
switch (status) {
|
|
129
|
+
case "ALLOW":
|
|
130
|
+
return color ? c("ALLOWED", ANSI.green, ANSI.bold) : "ALLOWED";
|
|
131
|
+
case "BLOCK":
|
|
132
|
+
return color ? c("BLOCKED", ANSI.red, ANSI.bold) : "BLOCKED";
|
|
133
|
+
case "PAUSE":
|
|
134
|
+
return color ? c("PAUSED", ANSI.yellow, ANSI.bold) : "PAUSED";
|
|
135
|
+
default:
|
|
136
|
+
return status;
|
|
137
|
+
}
|
|
147
138
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
type: 'tool_call',
|
|
158
|
-
tool: toolName,
|
|
159
|
-
intent: `${toolName}: ${JSON.stringify(params).slice(0, 100)}`,
|
|
160
|
-
args: params,
|
|
161
|
-
scope: deriveScope(toolName, params),
|
|
162
|
-
role: agentId ? roleMap.get(agentId) : undefined,
|
|
163
|
-
environment,
|
|
164
|
-
sessionOverrides: allowlist.getOverrides(),
|
|
165
|
-
};
|
|
139
|
+
function formatVerdictOneLine(verdict) {
|
|
140
|
+
const parts = [verdict.status];
|
|
141
|
+
if (verdict.ruleId) {
|
|
142
|
+
parts[0] += `: ${verdict.ruleId}`;
|
|
143
|
+
}
|
|
144
|
+
if (verdict.reason) {
|
|
145
|
+
parts.push(verdict.reason);
|
|
146
|
+
}
|
|
147
|
+
return parts.join(" \u2014 ");
|
|
166
148
|
}
|
|
167
|
-
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (file.toLowerCase().endsWith('.md')) {
|
|
176
|
-
const fullPath = (0, path_1.join)(dir, file);
|
|
177
|
-
mdFiles[file] = (0, fs_1.readFileSync)(fullPath, 'utf-8');
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
// Directory unreadable — return empty
|
|
183
|
-
}
|
|
184
|
-
return mdFiles;
|
|
149
|
+
|
|
150
|
+
// src/engine/condition-engine.ts
|
|
151
|
+
function getFieldValue(event, field) {
|
|
152
|
+
if (field.startsWith("args.")) {
|
|
153
|
+
const key = field.slice(5);
|
|
154
|
+
return event.args?.[key];
|
|
155
|
+
}
|
|
156
|
+
return event[field];
|
|
185
157
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
* Priority: explicit env var → /data if present (Docker) → cwd fallback.
|
|
192
|
-
*
|
|
193
|
-
* In OpenClaw Docker containers /data is always the mounted persistent volume.
|
|
194
|
-
* We detect its *presence* (existsSync), not its permissions (accessSync),
|
|
195
|
-
* because the node process may not yet have write access at register time
|
|
196
|
-
* on certain hosts (e.g. Hostinger), causing a false fallback to process.cwd().
|
|
197
|
-
*/
|
|
198
|
-
function getStorageRoot() {
|
|
199
|
-
// 1. Explicit override — always wins
|
|
200
|
-
if (process.env.OPENCLAW_DATA_DIR) {
|
|
201
|
-
return process.env.OPENCLAW_DATA_DIR;
|
|
202
|
-
}
|
|
203
|
-
// 2. /data exists — standard OpenClaw container volume
|
|
204
|
-
if ((0, fs_1.existsSync)('/data')) {
|
|
205
|
-
return '/data';
|
|
206
|
-
}
|
|
207
|
-
// 3. Local dev fallback
|
|
208
|
-
return process.cwd();
|
|
158
|
+
function toString(value) {
|
|
159
|
+
if (value === null || value === void 0) return "";
|
|
160
|
+
if (typeof value === "string") return value;
|
|
161
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
162
|
+
return JSON.stringify(value);
|
|
209
163
|
}
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
api.logger.debug?.(`[governance] ALLOW ${govEvent.intent} (session allowlist: ${guardId})`);
|
|
301
|
-
}
|
|
302
|
-
audit.logDecision(eventId, 'allow-always');
|
|
303
|
-
monitor?.recordAction(event.toolName, 'ALLOW', verdict.ruleId ?? undefined);
|
|
304
|
-
return; // allow — return void
|
|
305
|
-
}
|
|
306
|
-
// Auto-allow mode for CI/headless (spec §6 step 2)
|
|
307
|
-
// NEVER auto-allow governance-level pauses — these require human decision
|
|
308
|
-
if (autoAllow && verdict.severity !== 'governance') {
|
|
309
|
-
api.logger.info(`[governance] PAUSE ${govEvent.intent} (auto-allowed)`);
|
|
310
|
-
api.logger.info(` ${verdict.guard ?? verdict.ruleId}`);
|
|
311
|
-
audit.logDecision(eventId, 'allow-once');
|
|
312
|
-
return; // allow — return void
|
|
313
|
-
}
|
|
314
|
-
// Interactive prompt with full context (spec §6 step 3)
|
|
315
|
-
const guardLabel = verdict.guard ?? verdict.ruleId ?? 'governance rule';
|
|
316
|
-
const decision = await promptForDecision({
|
|
317
|
-
agentId: ctx.agentId,
|
|
318
|
-
role: govEvent.role,
|
|
319
|
-
tool: event.toolName,
|
|
320
|
-
scope: govEvent.scope,
|
|
321
|
-
intent: govEvent.intent,
|
|
322
|
-
guardLabel,
|
|
323
|
-
guardDescription: verdict.reason !== guardLabel ? verdict.reason : undefined,
|
|
324
|
-
evidence: verdict.evidence,
|
|
325
|
-
severity: verdict.severity,
|
|
326
|
-
});
|
|
327
|
-
// Log the decision (spec §7 — every PAUSE paired with a decision)
|
|
328
|
-
audit.logDecision(eventId, decision);
|
|
329
|
-
if (decision === 'allow-always') {
|
|
330
|
-
if (guardId)
|
|
331
|
-
allowlist.add(guardId);
|
|
332
|
-
process.stderr.write(`[governance] Added ${guardId} to session allowlist\n\n`);
|
|
333
|
-
monitor?.recordOverride();
|
|
334
|
-
// Track agent-approved .md modifications for bootstrap warning
|
|
335
|
-
if (verdict.severity === 'governance' && govEvent.scope?.toLowerCase().endsWith('.md')) {
|
|
336
|
-
agentModifiedMdFiles.set(govEvent.scope, { agentId: ctx.agentId ?? 'unknown', at: Date.now() });
|
|
337
|
-
}
|
|
338
|
-
return; // allow — return void
|
|
339
|
-
}
|
|
340
|
-
if (decision === 'allow-once') {
|
|
341
|
-
monitor?.recordOverride();
|
|
342
|
-
// Track agent-approved .md modifications for bootstrap warning
|
|
343
|
-
if (verdict.severity === 'governance' && govEvent.scope?.toLowerCase().endsWith('.md')) {
|
|
344
|
-
agentModifiedMdFiles.set(govEvent.scope, { agentId: ctx.agentId ?? 'unknown', at: Date.now() });
|
|
345
|
-
}
|
|
346
|
-
return; // allow — return void
|
|
347
|
-
}
|
|
348
|
-
// deny
|
|
349
|
-
process.stderr.write(`[governance] Denied\n\n`);
|
|
350
|
-
return { block: true, blockReason: verdict.reason };
|
|
351
|
-
}
|
|
352
|
-
// ── ALLOW ──────────────────────────────────────────────
|
|
353
|
-
if (verbose) {
|
|
354
|
-
api.logger.debug?.(`[governance] ALLOW ${govEvent.intent}`);
|
|
355
|
-
}
|
|
356
|
-
// allow — return void
|
|
357
|
-
});
|
|
358
|
-
// ── Session Reset Hook — Drift detection on /new ────────────────
|
|
359
|
-
// OpenClaw before_reset hook (from src/plugins/types.ts):
|
|
360
|
-
// event: { sessionFile?: string; messages?: unknown[]; reason?: string }
|
|
361
|
-
// ctx: { agentId?: string; sessionKey?: string }
|
|
362
|
-
// return: void
|
|
363
|
-
//
|
|
364
|
-
// On /new or /reset, check if .md files have changed since last bootstrap.
|
|
365
|
-
// If drifted, prompt the user: "Regenerate constitution? [y/n]"
|
|
366
|
-
// If yes, re-bootstrap from current .md files before the new session starts.
|
|
367
|
-
api.on('before_reset', async () => {
|
|
368
|
-
// Only check if we have a world with hash baseline
|
|
369
|
-
const world = engine.getWorld();
|
|
370
|
-
const storedHashes = world?.metadata?.mdHashes;
|
|
371
|
-
if (!storedHashes)
|
|
372
|
-
return;
|
|
373
|
-
const drift = (0, world_bootstrap_1.checkMdDrift)(workspaceDir, storedHashes);
|
|
374
|
-
const hasDrift = drift.changed.length > 0 || drift.added.length > 0 || drift.removed.length > 0;
|
|
375
|
-
if (!hasDrift) {
|
|
376
|
-
// Clear session allowlist on reset (fresh session = fresh allowlist)
|
|
377
|
-
allowlist.clear();
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
// Report what changed
|
|
381
|
-
const changedSummary = [];
|
|
382
|
-
if (drift.changed.length > 0)
|
|
383
|
-
changedSummary.push(`modified: ${drift.changed.join(', ')}`);
|
|
384
|
-
if (drift.added.length > 0)
|
|
385
|
-
changedSummary.push(`new: ${drift.added.join(', ')}`);
|
|
386
|
-
if (drift.removed.length > 0)
|
|
387
|
-
changedSummary.push(`removed: ${drift.removed.join(', ')}`);
|
|
388
|
-
process.stderr.write(`\n[NeuroVerseOS] Markdown files changed since last bootstrap.\n`);
|
|
389
|
-
process.stderr.write(` ${changedSummary.join('; ')}\n`);
|
|
390
|
-
// Prompt: regenerate?
|
|
391
|
-
const answer = await new Promise((res) => {
|
|
392
|
-
const rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stderr });
|
|
393
|
-
process.stderr.write(` Regenerate constitution? [y/n]: `);
|
|
394
|
-
rl.on('line', (line) => { rl.close(); res(line.trim().toLowerCase()); });
|
|
395
|
-
rl.on('close', () => res('n'));
|
|
396
|
-
});
|
|
397
|
-
if (answer === 'y' || answer === 'yes') {
|
|
398
|
-
const mdFiles = readMdFiles(workspaceDir);
|
|
399
|
-
if (Object.keys(mdFiles).length > 0) {
|
|
400
|
-
const newWorld = (0, world_bootstrap_1.bootstrapWorldFromMarkdown)(mdFiles);
|
|
401
|
-
engine.setWorld(newWorld);
|
|
402
|
-
engine.saveWorld();
|
|
403
|
-
process.stderr.write(`[NeuroVerseOS] Constitution regenerated. ${newWorld.invariants.length} invariants, ${newWorld.guards.length} guards, ${newWorld.rules.length} rules.\n\n`);
|
|
404
|
-
}
|
|
405
|
-
else {
|
|
406
|
-
process.stderr.write(`[NeuroVerseOS] No .md files found. Keeping current constitution.\n\n`);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
else {
|
|
410
|
-
process.stderr.write(`[NeuroVerseOS] Keeping current constitution.\n\n`);
|
|
411
|
-
}
|
|
412
|
-
// Always clear session allowlist and alert history on reset
|
|
413
|
-
allowlist.clear();
|
|
414
|
-
engine.clearAlertHistory();
|
|
415
|
-
});
|
|
416
|
-
// ── Background Drift Service ───────────────────────────────────
|
|
417
|
-
if (monitor) {
|
|
418
|
-
api.registerService({
|
|
419
|
-
id: 'neuroverse-drift-monitor',
|
|
420
|
-
start: () => {
|
|
421
|
-
monitor.start();
|
|
422
|
-
api.logger.info('[NeuroVerseOS] Drift monitor started');
|
|
423
|
-
},
|
|
424
|
-
stop: () => {
|
|
425
|
-
monitor.stop();
|
|
426
|
-
api.logger.info('[NeuroVerseOS] Drift monitor stopped');
|
|
427
|
-
},
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
// ── Agent Tool: governance_status ──────────────────────────────
|
|
431
|
-
// Lets agents answer "am I protected?", "any issues?", "what's enforced?"
|
|
432
|
-
// by querying structured kernel state. No guessing — deterministic output.
|
|
433
|
-
api.registerTool({
|
|
434
|
-
name: 'governance_status',
|
|
435
|
-
description: 'Query the governance kernel state. Use when the user asks about protection status, pending changes, integrity, role bindings, or audit trail.',
|
|
436
|
-
parameters: {
|
|
437
|
-
type: 'object',
|
|
438
|
-
properties: {
|
|
439
|
-
query: {
|
|
440
|
-
type: 'string',
|
|
441
|
-
enum: ['summary', 'status', 'bindings', 'audit', 'integrity'],
|
|
442
|
-
description: 'What aspect of governance to query',
|
|
443
|
-
},
|
|
444
|
-
},
|
|
445
|
-
required: ['query'],
|
|
446
|
-
},
|
|
447
|
-
handler: async (args) => {
|
|
448
|
-
const world = engine.getWorld();
|
|
449
|
-
const stats = engine.getStatus();
|
|
450
|
-
const auditCounts = audit.getCounts();
|
|
451
|
-
const bindings = engine.getRoleBindings();
|
|
452
|
-
const meta = engine.getMetaRecord();
|
|
453
|
-
if (!world) {
|
|
454
|
-
return { text: 'No governance world loaded. The user should run /world bootstrap to create one from their .md files.' };
|
|
455
|
-
}
|
|
456
|
-
switch (args.query) {
|
|
457
|
-
case 'summary': {
|
|
458
|
-
const ruleCount = stats.invariantCount + stats.guardCount + stats.ruleCount;
|
|
459
|
-
const issues = [];
|
|
460
|
-
const storedHashes = world.metadata?.mdHashes;
|
|
461
|
-
if (storedHashes) {
|
|
462
|
-
const drift = (0, world_bootstrap_1.checkMdDrift)(workspaceDir, storedHashes);
|
|
463
|
-
if (drift.changed.length > 0 || drift.added.length > 0 || drift.removed.length > 0) {
|
|
464
|
-
issues.push('source files have drifted from the active world');
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
if ((0, fs_1.existsSync)((0, path_1.join)(worldDir, 'world.pending.json'))) {
|
|
468
|
-
issues.push('pending governance changes await approval');
|
|
469
|
-
}
|
|
470
|
-
if (bindings.length === 0 && stats.roleCount > 0) {
|
|
471
|
-
issues.push('roles are defined but no agents are bound to them');
|
|
472
|
-
}
|
|
473
|
-
return {
|
|
474
|
-
text: JSON.stringify({
|
|
475
|
-
worldActive: true,
|
|
476
|
-
worldName: world.metadata.name,
|
|
477
|
-
enforcement: stats.enforcement,
|
|
478
|
-
ruleCount,
|
|
479
|
-
roleCount: stats.roleCount,
|
|
480
|
-
bindingCount: bindings.length,
|
|
481
|
-
verdicts: auditCounts,
|
|
482
|
-
issues,
|
|
483
|
-
}),
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
case 'bindings': {
|
|
487
|
-
const bindingList = bindings.map(b => {
|
|
488
|
-
const role = world.roles.find(r => r.id === b.roleId);
|
|
489
|
-
return {
|
|
490
|
-
agentId: b.agentId,
|
|
491
|
-
roleId: b.roleId,
|
|
492
|
-
roleName: role?.name ?? '(unknown)',
|
|
493
|
-
boundBy: b.boundBy,
|
|
494
|
-
};
|
|
495
|
-
});
|
|
496
|
-
return {
|
|
497
|
-
text: JSON.stringify({
|
|
498
|
-
totalBindings: bindings.length,
|
|
499
|
-
totalRoles: stats.roleCount,
|
|
500
|
-
availableRoles: world.roles.map(r => ({ id: r.id, name: r.name })),
|
|
501
|
-
bindings: bindingList,
|
|
502
|
-
}),
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
case 'audit': {
|
|
506
|
-
return {
|
|
507
|
-
text: JSON.stringify({
|
|
508
|
-
totalVerdicts: auditCounts.total,
|
|
509
|
-
allowed: auditCounts.allow,
|
|
510
|
-
paused: auditCounts.pause,
|
|
511
|
-
blocked: auditCounts.block,
|
|
512
|
-
driftStats: monitor?.getStats() ?? null,
|
|
513
|
-
}),
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
case 'integrity': {
|
|
517
|
-
let driftStatus = 'unknown';
|
|
518
|
-
const storedHashes = world.metadata?.mdHashes;
|
|
519
|
-
if (storedHashes) {
|
|
520
|
-
const drift = (0, world_bootstrap_1.checkMdDrift)(workspaceDir, storedHashes);
|
|
521
|
-
const hasDrift = drift.changed.length > 0 || drift.added.length > 0 || drift.removed.length > 0;
|
|
522
|
-
driftStatus = hasDrift ? 'drifted' : 'in_sync';
|
|
523
|
-
}
|
|
524
|
-
return {
|
|
525
|
-
text: JSON.stringify({
|
|
526
|
-
worldHash: meta?.hash?.slice(0, 16) ?? null,
|
|
527
|
-
version: meta?.version ?? null,
|
|
528
|
-
activatedAt: meta?.activatedAt ?? null,
|
|
529
|
-
sourceDrift: driftStatus,
|
|
530
|
-
pendingChanges: (0, fs_1.existsSync)((0, path_1.join)(worldDir, 'world.pending.json')),
|
|
531
|
-
}),
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
default:
|
|
535
|
-
return { text: 'Unknown query. Use: summary, status, bindings, audit, or integrity.' };
|
|
536
|
-
}
|
|
537
|
-
},
|
|
538
|
-
});
|
|
539
|
-
// ── Agent Tool: world_proposal.create ──────────────────────────
|
|
540
|
-
api.registerTool({
|
|
541
|
-
name: 'world_proposal_create',
|
|
542
|
-
description: 'Propose a governance amendment. The human must approve it with /world approve.',
|
|
543
|
-
parameters: {
|
|
544
|
-
type: 'object',
|
|
545
|
-
properties: {
|
|
546
|
-
type: { type: 'string', enum: ['add_guard', 'modify_threshold', 'add_role', 'add_invariant'] },
|
|
547
|
-
reason: { type: 'string', description: 'Why this amendment is needed' },
|
|
548
|
-
suggestion: { type: 'string', description: 'The specific change to make' },
|
|
549
|
-
evidence: { type: 'string', description: 'What behavior triggered this recommendation' },
|
|
550
|
-
},
|
|
551
|
-
required: ['type', 'reason', 'suggestion'],
|
|
552
|
-
},
|
|
553
|
-
handler: async (args) => {
|
|
554
|
-
const id = Date.now().toString(36);
|
|
555
|
-
const proposal = { ...args, id, createdAt: Date.now(), source: 'agent' };
|
|
556
|
-
const proposalPath = (0, path_1.join)(proposalsDir, `${id}.json`);
|
|
557
|
-
(0, fs_1.writeFileSync)(proposalPath, JSON.stringify(proposal, null, 2));
|
|
558
|
-
return {
|
|
559
|
-
text: `Governance amendment proposed (ID: ${id}).\nType: ${args.type}\nReason: ${args.reason}\nThe human must run /world approve ${id} to apply this change.`,
|
|
560
|
-
};
|
|
561
|
-
},
|
|
562
|
-
});
|
|
563
|
-
// ── CLI Commands (gateway CLI: `openclaw world ...`) ───────────
|
|
564
|
-
api.registerCli(({ program, logger }) => {
|
|
565
|
-
const worldCmd = program
|
|
566
|
-
.command('world')
|
|
567
|
-
.description('NeuroVerse governance commands');
|
|
568
|
-
// openclaw world bootstrap
|
|
569
|
-
worldCmd
|
|
570
|
-
.command('bootstrap')
|
|
571
|
-
.description('Generate world.json from your .md files')
|
|
572
|
-
.action(async () => {
|
|
573
|
-
logger.info('[NeuroVerseOS] Reading .md files from workspace...');
|
|
574
|
-
const mdFiles = readMdFiles(workspaceDir);
|
|
575
|
-
for (const file of Object.keys(mdFiles)) {
|
|
576
|
-
logger.info(` Found ${file}`);
|
|
577
|
-
}
|
|
578
|
-
if (Object.keys(mdFiles).length === 0) {
|
|
579
|
-
logger.info(' No .md files found in workspace.');
|
|
580
|
-
logger.info(' Add any .md files to define your governance rules.');
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
logger.info(`[NeuroVerseOS] Bootstrapping world from ${Object.keys(mdFiles).length} files...`);
|
|
584
|
-
const world = (0, world_bootstrap_1.bootstrapWorldFromMarkdown)(mdFiles);
|
|
585
|
-
engine.setWorld(world);
|
|
586
|
-
engine.saveWorld();
|
|
587
|
-
logger.info(`World file created: ${worldPath}`);
|
|
588
|
-
logger.info(` Kernel: ${world.kernel.enforcementMode} mode`);
|
|
589
|
-
logger.info(` Invariants: ${world.invariants.length}`);
|
|
590
|
-
logger.info(` Guards: ${world.guards.length}`);
|
|
591
|
-
logger.info(` Rules: ${world.rules.length}`);
|
|
592
|
-
logger.info(` Roles: ${world.roles.length}`);
|
|
593
|
-
});
|
|
594
|
-
// openclaw world status
|
|
595
|
-
worldCmd
|
|
596
|
-
.command('status')
|
|
597
|
-
.description('Show governance status')
|
|
598
|
-
.action(() => {
|
|
599
|
-
if (!(0, fs_1.existsSync)(worldPath)) {
|
|
600
|
-
logger.info('[NeuroVerseOS] No world file found. Run /world bootstrap first.');
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
const stats = engine.getStatus();
|
|
604
|
-
const auditCounts = audit.getCounts();
|
|
605
|
-
logger.info(`World: ${worldPath}`);
|
|
606
|
-
logger.info(`Enforcement: ${config.enforcement ?? 'standard'}`);
|
|
607
|
-
logger.info(`Invariants: ${stats.invariantCount}, Guards: ${stats.guardCount}, Rules: ${stats.ruleCount}, Roles: ${stats.roleCount}`);
|
|
608
|
-
logger.info(`Verdicts — ALLOW: ${auditCounts.allow}, PAUSE: ${auditCounts.pause}, BLOCK: ${auditCounts.block}`);
|
|
609
|
-
});
|
|
610
|
-
// openclaw world laws
|
|
611
|
-
worldCmd
|
|
612
|
-
.command('laws')
|
|
613
|
-
.description('Display the full constitution')
|
|
614
|
-
.action(() => {
|
|
615
|
-
if (!(0, fs_1.existsSync)(worldPath)) {
|
|
616
|
-
logger.info('[NeuroVerseOS] No world file found. Run /world bootstrap first.');
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
const world = engine.getWorld();
|
|
620
|
-
if (!world) {
|
|
621
|
-
logger.info('[NeuroVerseOS] World file could not be loaded.');
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
logger.info(`\n=== THE LAWS OF ${(world.metadata.name ?? 'NeuroVerseOS').toUpperCase()} ===\n`);
|
|
625
|
-
if (world.metadata.description)
|
|
626
|
-
logger.info(world.metadata.description);
|
|
627
|
-
logger.info(`Enforcement: ${world.kernel.enforcementMode} | Overrides: ${world.kernel.sessionOverridesAllowed ? 'allowed' : 'disabled'}\n`);
|
|
628
|
-
logger.info(`--- INVARIANTS (${world.invariants.length}) ---`);
|
|
629
|
-
for (const inv of world.invariants) {
|
|
630
|
-
logger.info(` [${inv.id}] ${inv.description}`);
|
|
631
|
-
logger.info(` When: ${inv.condition.field} ${inv.condition.operator} ${JSON.stringify(inv.condition.value)}`);
|
|
632
|
-
}
|
|
633
|
-
logger.info(`\n--- GUARDS (${world.guards.length}) ---`);
|
|
634
|
-
for (const guard of world.guards) {
|
|
635
|
-
logger.info(` [${guard.id}] → ${guard.enforcement.toUpperCase()}${guard.description ? ': ' + guard.description : ''}`);
|
|
636
|
-
logger.info(` When: ${guard.condition.field} ${guard.condition.operator} ${JSON.stringify(guard.condition.value)}`);
|
|
637
|
-
if (guard.appliesTo.length > 0)
|
|
638
|
-
logger.info(` Applies to: ${guard.appliesTo.join(', ')}`);
|
|
639
|
-
}
|
|
640
|
-
logger.info(`\n--- RULES (${world.rules.length}) ---`);
|
|
641
|
-
for (const rule of world.rules) {
|
|
642
|
-
logger.info(` [${rule.id}] → ${rule.effect.verdict.toUpperCase()}${rule.description ? ': ' + rule.description : ''}`);
|
|
643
|
-
logger.info(` Trigger: ${rule.trigger.field} ${rule.trigger.operator} ${JSON.stringify(rule.trigger.value)}`);
|
|
644
|
-
}
|
|
645
|
-
logger.info(`\n--- ROLES (${world.roles.length}) ---`);
|
|
646
|
-
for (const role of world.roles) {
|
|
647
|
-
logger.info(` [${role.id}] ${role.name}`);
|
|
648
|
-
if (role.canDo.length > 0)
|
|
649
|
-
logger.info(` Can do: ${role.canDo.join(', ')}`);
|
|
650
|
-
if (role.cannotDo.length > 0)
|
|
651
|
-
logger.info(` Cannot do: ${role.cannotDo.join(', ')}`);
|
|
652
|
-
logger.info(` Requires approval: ${role.requiresApproval ? 'yes' : 'no'}`);
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
}, { commands: ['world'] });
|
|
656
|
-
// ── Chat Command: /world ───────────────────────────────────────
|
|
657
|
-
// Registers as a slash command in the agent conversation.
|
|
658
|
-
// Bypasses the LLM — processed directly by the plugin.
|
|
659
|
-
api.registerCommand({
|
|
660
|
-
name: 'world',
|
|
661
|
-
description: 'NeuroVerse governance commands (bootstrap, status, laws, propose, approve, export)',
|
|
662
|
-
acceptsArgs: true,
|
|
663
|
-
requireAuth: true,
|
|
664
|
-
handler: async (ctx) => {
|
|
665
|
-
const cmdArgs = (ctx.args ?? '').trim();
|
|
666
|
-
const [subcommand, ...rest] = cmdArgs.split(/\s+/);
|
|
667
|
-
// ── /world (no args) — Quick status dashboard ──
|
|
668
|
-
if (!subcommand) {
|
|
669
|
-
if (!(0, fs_1.existsSync)(worldPath)) {
|
|
670
|
-
return {
|
|
671
|
-
text: [
|
|
672
|
-
'**NeuroVerse Governance**',
|
|
673
|
-
'',
|
|
674
|
-
'No world file found. Run `/world bootstrap` to create one from your .md files.',
|
|
675
|
-
'Type `/world help` for all commands.',
|
|
676
|
-
].join('\n'),
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
const world = engine.getWorld();
|
|
680
|
-
const stats = engine.getStatus();
|
|
681
|
-
const auditCounts = audit.getCounts();
|
|
682
|
-
const bindings = engine.getRoleBindings();
|
|
683
|
-
const meta = engine.getMetaRecord();
|
|
684
|
-
const worldName = world?.metadata?.name ?? 'NeuroVerseOS';
|
|
685
|
-
// Integrity check
|
|
686
|
-
let integrityStatus = 'Verified';
|
|
687
|
-
if (!meta) {
|
|
688
|
-
integrityStatus = 'No baseline (run /world bootstrap)';
|
|
689
|
-
}
|
|
690
|
-
// Drift check
|
|
691
|
-
let driftStatus = 'In sync';
|
|
692
|
-
const storedHashes = world?.metadata?.mdHashes;
|
|
693
|
-
if (storedHashes) {
|
|
694
|
-
const drift = (0, world_bootstrap_1.checkMdDrift)(workspaceDir, storedHashes);
|
|
695
|
-
const hasDrift = drift.changed.length > 0 || drift.added.length > 0 || drift.removed.length > 0;
|
|
696
|
-
if (hasDrift) {
|
|
697
|
-
const parts = [];
|
|
698
|
-
if (drift.changed.length > 0)
|
|
699
|
-
parts.push(`${drift.changed.length} modified`);
|
|
700
|
-
if (drift.added.length > 0)
|
|
701
|
-
parts.push(`${drift.added.length} new`);
|
|
702
|
-
if (drift.removed.length > 0)
|
|
703
|
-
parts.push(`${drift.removed.length} removed`);
|
|
704
|
-
driftStatus = parts.join(', ') + ' — run /world bootstrap';
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
else {
|
|
708
|
-
driftStatus = 'No baseline';
|
|
709
|
-
}
|
|
710
|
-
// Pending check
|
|
711
|
-
const pendingPath = (0, path_1.join)(worldDir, 'world.pending.json');
|
|
712
|
-
const hasPending = (0, fs_1.existsSync)(pendingPath);
|
|
713
|
-
const lines = [
|
|
714
|
-
`**${worldName}** — NeuroVerse Governance`,
|
|
715
|
-
'',
|
|
716
|
-
`World: ACTIVE`,
|
|
717
|
-
`Integrity: ${integrityStatus}`,
|
|
718
|
-
`Enforcement: ${stats.enforcement}`,
|
|
719
|
-
`Source drift: ${driftStatus}`,
|
|
720
|
-
`Pending updates: ${hasPending ? 'Yes — run /world approve' : 'None'}`,
|
|
721
|
-
'',
|
|
722
|
-
`Invariants: ${stats.invariantCount} Guards: ${stats.guardCount} Rules: ${stats.ruleCount} Roles: ${stats.roleCount}`,
|
|
723
|
-
`Role bindings: ${bindings.length > 0 ? `${bindings.length} active` : 'None — run /world bind'}`,
|
|
724
|
-
'',
|
|
725
|
-
`Verdicts this session: ${auditCounts.allow} allowed, ${auditCounts.pause} paused, ${auditCounts.block} blocked`,
|
|
726
|
-
];
|
|
727
|
-
return { text: lines.join('\n') };
|
|
728
|
-
}
|
|
729
|
-
// ── /world help ──
|
|
730
|
-
if (subcommand === 'help') {
|
|
731
|
-
return {
|
|
732
|
-
text: [
|
|
733
|
-
'**NeuroVerse Governance Commands**',
|
|
734
|
-
'',
|
|
735
|
-
'`/world` — Quick status dashboard',
|
|
736
|
-
'`/world summary` — One-line protection status',
|
|
737
|
-
'`/world status` — Full metrics (drift, audit, friction)',
|
|
738
|
-
'`/world laws` — Read the full constitution',
|
|
739
|
-
'`/world bootstrap` — Generate world.json from your .md files',
|
|
740
|
-
'',
|
|
741
|
-
'**Role bindings:**',
|
|
742
|
-
'`/world bind <agentId> <roleId>` — Bind an agent to a role',
|
|
743
|
-
'`/world unbind <agentId>` — Remove a binding',
|
|
744
|
-
'`/world bindings` — Show all bindings',
|
|
745
|
-
'',
|
|
746
|
-
'**Amendments:**',
|
|
747
|
-
'`/world propose` — Agent recommends amendments',
|
|
748
|
-
'`/world approve <id>` — Approve amendment',
|
|
749
|
-
'`/world export` — Export world file',
|
|
750
|
-
'',
|
|
751
|
-
'You can also ask naturally:',
|
|
752
|
-
' "Am I protected?" "Any pending changes?" "What changed?"',
|
|
753
|
-
].join('\n'),
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
// ── /world summary ── One-line conversational status
|
|
757
|
-
if (subcommand === 'summary') {
|
|
758
|
-
if (!(0, fs_1.existsSync)(worldPath)) {
|
|
759
|
-
return { text: 'No governance world loaded. Run `/world bootstrap` to create one.' };
|
|
760
|
-
}
|
|
761
|
-
const stats = engine.getStatus();
|
|
762
|
-
const bindings = engine.getRoleBindings();
|
|
763
|
-
const auditCounts = audit.getCounts();
|
|
764
|
-
// Check for issues
|
|
765
|
-
const issues = [];
|
|
766
|
-
const storedHashes = engine.getWorld()?.metadata?.mdHashes;
|
|
767
|
-
if (storedHashes) {
|
|
768
|
-
const drift = (0, world_bootstrap_1.checkMdDrift)(workspaceDir, storedHashes);
|
|
769
|
-
if (drift.changed.length > 0 || drift.added.length > 0 || drift.removed.length > 0) {
|
|
770
|
-
issues.push('source drift detected');
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
const pendingPath = (0, path_1.join)(worldDir, 'world.pending.json');
|
|
774
|
-
if ((0, fs_1.existsSync)(pendingPath)) {
|
|
775
|
-
issues.push('pending updates await approval');
|
|
776
|
-
}
|
|
777
|
-
if (bindings.length === 0 && stats.roleCount > 0) {
|
|
778
|
-
issues.push('roles defined but no agents bound');
|
|
779
|
-
}
|
|
780
|
-
const enforcement = stats.enforcement;
|
|
781
|
-
const ruleCount = stats.invariantCount + stats.guardCount + stats.ruleCount;
|
|
782
|
-
if (issues.length === 0) {
|
|
783
|
-
return {
|
|
784
|
-
text: `Governance active. ${ruleCount} rules enforcing in ${enforcement} mode. ${bindings.length} agent(s) bound. ${auditCounts.total} verdicts logged. No issues.`,
|
|
785
|
-
};
|
|
786
|
-
}
|
|
787
|
-
return {
|
|
788
|
-
text: `Governance active. ${ruleCount} rules enforcing in ${enforcement} mode. ${bindings.length} agent(s) bound. ${auditCounts.total} verdicts logged. Attention: ${issues.join('; ')}.`,
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
// ── /world bootstrap ──
|
|
792
|
-
if (subcommand === 'bootstrap') {
|
|
793
|
-
const lines = [];
|
|
794
|
-
// Warn if agents modified .md files during this session
|
|
795
|
-
if (agentModifiedMdFiles.size > 0) {
|
|
796
|
-
lines.push('🔒 GOVERNANCE WARNING');
|
|
797
|
-
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
798
|
-
lines.push('The following .md files were modified by agents this session:');
|
|
799
|
-
for (const [file, { agentId }] of agentModifiedMdFiles) {
|
|
800
|
-
lines.push(` ⚠️ ${file} (by ${agentId})`);
|
|
801
|
-
}
|
|
802
|
-
lines.push('');
|
|
803
|
-
lines.push('Bootstrapping will compile these changes into your World File.');
|
|
804
|
-
lines.push('Review each file before proceeding. If any changes look');
|
|
805
|
-
lines.push('unexpected, revert them before bootstrapping.');
|
|
806
|
-
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
807
|
-
lines.push('');
|
|
808
|
-
}
|
|
809
|
-
lines.push('[NeuroVerseOS] Reading .md files from workspace...');
|
|
810
|
-
const mdFiles = readMdFiles(workspaceDir);
|
|
811
|
-
for (const file of Object.keys(mdFiles)) {
|
|
812
|
-
lines.push(` Found ${file}`);
|
|
813
|
-
}
|
|
814
|
-
if (Object.keys(mdFiles).length === 0) {
|
|
815
|
-
lines.push(' No .md files found in workspace.');
|
|
816
|
-
lines.push(' Add any .md files to define your governance rules.');
|
|
817
|
-
return { text: lines.join('\n') };
|
|
818
|
-
}
|
|
819
|
-
lines.push(`\n[NeuroVerseOS] Bootstrapping world from ${Object.keys(mdFiles).length} files...`);
|
|
820
|
-
const world = (0, world_bootstrap_1.bootstrapWorldFromMarkdown)(mdFiles);
|
|
821
|
-
engine.setWorld(world);
|
|
822
|
-
engine.saveWorld();
|
|
823
|
-
lines.push(`\nWorld file created: ${worldPath}`);
|
|
824
|
-
lines.push(` Kernel: ${world.kernel.enforcementMode} mode`);
|
|
825
|
-
lines.push(` Session overrides: ${world.kernel.sessionOverridesAllowed ? 'allowed' : 'disabled'}`);
|
|
826
|
-
lines.push(` Invariants: ${world.invariants.length}`);
|
|
827
|
-
lines.push(` Guards: ${world.guards.length}`);
|
|
828
|
-
lines.push(` Rules: ${world.rules.length}`);
|
|
829
|
-
lines.push(` Roles: ${world.roles.length}`);
|
|
830
|
-
lines.push(`\n Run '/world status' to see governance overview.`);
|
|
831
|
-
return { text: lines.join('\n') };
|
|
832
|
-
}
|
|
833
|
-
// ── /world status ──
|
|
834
|
-
if (subcommand === 'status') {
|
|
835
|
-
if (!(0, fs_1.existsSync)(worldPath)) {
|
|
836
|
-
return { text: '[NeuroVerseOS] No world file found. Run /world bootstrap first.' };
|
|
837
|
-
}
|
|
838
|
-
const stats = engine.getStatus();
|
|
839
|
-
const driftStats = monitor?.getStats();
|
|
840
|
-
const auditCounts = audit.getCounts();
|
|
841
|
-
const lines = [
|
|
842
|
-
'\n=== NeuroVerse Governance Status ===\n',
|
|
843
|
-
`World: ${worldPath}`,
|
|
844
|
-
`Enforcement: ${config.enforcement ?? 'standard'}`,
|
|
845
|
-
`Mode: ${config.observeFirst ? 'OBSERVE (recording only)' : 'ENFORCE (active governance)'}`,
|
|
846
|
-
`\nInvariants: ${stats.invariantCount}`,
|
|
847
|
-
`Guards: ${stats.guardCount}`,
|
|
848
|
-
`Rules: ${stats.ruleCount}`,
|
|
849
|
-
`Roles: ${stats.roleCount}`,
|
|
850
|
-
`Role bindings: ${engine.getRoleBindings().length}`,
|
|
851
|
-
`\n--- Audit Log ---`,
|
|
852
|
-
`Total verdicts: ${auditCounts.total}`,
|
|
853
|
-
` ALLOW: ${auditCounts.allow}`,
|
|
854
|
-
` PAUSE: ${auditCounts.pause}`,
|
|
855
|
-
` BLOCK: ${auditCounts.block}`,
|
|
856
|
-
];
|
|
857
|
-
if (driftStats) {
|
|
858
|
-
lines.push(`\n--- Drift Metrics ---`, `Total actions: ${driftStats.totalActions}`, `Blocks: ${driftStats.blockCount} (${(driftStats.blockRate * 100).toFixed(1)}%)`, `Pauses: ${driftStats.pauseCount} (${(driftStats.pauseRate * 100).toFixed(1)}%)`, `Allows: ${driftStats.allowCount}`, `Override rate: ${(driftStats.overrideRate * 100).toFixed(1)}%`);
|
|
859
|
-
if (driftStats.driftSignals.length > 0) {
|
|
860
|
-
lines.push(`\nDrift signals:`);
|
|
861
|
-
for (const signal of driftStats.driftSignals) {
|
|
862
|
-
lines.push(` - ${signal}`);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
// ── Markdown Drift Detection ──
|
|
867
|
-
const world = engine.getWorld();
|
|
868
|
-
const storedHashes = world?.metadata?.mdHashes;
|
|
869
|
-
if (storedHashes) {
|
|
870
|
-
const drift = (0, world_bootstrap_1.checkMdDrift)(workspaceDir, storedHashes);
|
|
871
|
-
const hasDrift = drift.changed.length > 0 || drift.added.length > 0 || drift.removed.length > 0;
|
|
872
|
-
if (hasDrift) {
|
|
873
|
-
lines.push(`\n--- Constitution Drift ---`);
|
|
874
|
-
lines.push(`Markdown files changed since last bootstrap.`);
|
|
875
|
-
if (drift.changed.length > 0)
|
|
876
|
-
lines.push(` Modified: ${drift.changed.join(', ')}`);
|
|
877
|
-
if (drift.added.length > 0)
|
|
878
|
-
lines.push(` New: ${drift.added.join(', ')}`);
|
|
879
|
-
if (drift.removed.length > 0)
|
|
880
|
-
lines.push(` Removed: ${drift.removed.join(', ')}`);
|
|
881
|
-
lines.push(`Governance may be out of sync. Run /world bootstrap to regenerate.`);
|
|
882
|
-
}
|
|
883
|
-
else {
|
|
884
|
-
lines.push(`\nConstitution: in sync with .md files`);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
else {
|
|
888
|
-
lines.push(`\nConstitution: no hash baseline (bootstrapped before drift detection was added)`);
|
|
889
|
-
lines.push(`Run /world bootstrap to establish baseline.`);
|
|
890
|
-
}
|
|
891
|
-
return { text: lines.join('\n') };
|
|
892
|
-
}
|
|
893
|
-
// ── /world laws ──
|
|
894
|
-
if (subcommand === 'laws') {
|
|
895
|
-
if (!(0, fs_1.existsSync)(worldPath)) {
|
|
896
|
-
return { text: '[NeuroVerseOS] No world file found. Run /world bootstrap first.' };
|
|
897
|
-
}
|
|
898
|
-
const world = engine.getWorld();
|
|
899
|
-
if (!world) {
|
|
900
|
-
return { text: '[NeuroVerseOS] World file could not be loaded.' };
|
|
901
|
-
}
|
|
902
|
-
const lines = [
|
|
903
|
-
'\n========================================',
|
|
904
|
-
` THE LAWS OF ${(world.metadata.name ?? 'NeuroVerseOS').toUpperCase()}`,
|
|
905
|
-
'========================================\n',
|
|
906
|
-
];
|
|
907
|
-
if (world.metadata.description) {
|
|
908
|
-
lines.push(`${world.metadata.description}\n`);
|
|
909
|
-
}
|
|
910
|
-
lines.push(`Bootstrapped from: ${world.metadata.bootstrappedFrom.join(', ')}`);
|
|
911
|
-
lines.push(`Enforcement mode: ${world.kernel.enforcementMode}`);
|
|
912
|
-
lines.push(`Session overrides: ${world.kernel.sessionOverridesAllowed ? 'allowed' : 'disabled'}`);
|
|
913
|
-
// ── Invariants ──
|
|
914
|
-
lines.push(`\n--- INVARIANTS (${world.invariants.length}) ---`);
|
|
915
|
-
lines.push(`Hard blocks. Cannot be overridden. Ever.\n`);
|
|
916
|
-
if (world.invariants.length === 0) {
|
|
917
|
-
lines.push(' (none)');
|
|
918
|
-
}
|
|
919
|
-
for (const inv of world.invariants) {
|
|
920
|
-
lines.push(` [${inv.id}]`);
|
|
921
|
-
lines.push(` ${inv.description}`);
|
|
922
|
-
lines.push(` When: ${inv.condition.field} ${inv.condition.operator} ${JSON.stringify(inv.condition.value)}`);
|
|
923
|
-
if (inv.scope.length > 0)
|
|
924
|
-
lines.push(` Scope: ${inv.scope.join(', ')}`);
|
|
925
|
-
lines.push('');
|
|
926
|
-
}
|
|
927
|
-
// ── Guards ──
|
|
928
|
-
lines.push(`--- GUARDS (${world.guards.length}) ---`);
|
|
929
|
-
lines.push(`Pause or block. Can be session-overridden if allowed.\n`);
|
|
930
|
-
if (world.guards.length === 0) {
|
|
931
|
-
lines.push(' (none)');
|
|
932
|
-
}
|
|
933
|
-
for (const guard of world.guards) {
|
|
934
|
-
lines.push(` [${guard.id}] → ${guard.enforcement.toUpperCase()}`);
|
|
935
|
-
if (guard.description)
|
|
936
|
-
lines.push(` ${guard.description}`);
|
|
937
|
-
lines.push(` When: ${guard.condition.field} ${guard.condition.operator} ${JSON.stringify(guard.condition.value)}`);
|
|
938
|
-
if (guard.appliesTo.length > 0)
|
|
939
|
-
lines.push(` Applies to: ${guard.appliesTo.join(', ')}`);
|
|
940
|
-
if (guard.scope.length > 0)
|
|
941
|
-
lines.push(` Scope: ${guard.scope.join(', ')}`);
|
|
942
|
-
if (guard.requiresApproval)
|
|
943
|
-
lines.push(` Requires approval: yes`);
|
|
944
|
-
lines.push('');
|
|
945
|
-
}
|
|
946
|
-
// ── Rules ──
|
|
947
|
-
lines.push(`--- RULES (${world.rules.length}) ---`);
|
|
948
|
-
lines.push(`Contextual triggers with verdict effects.\n`);
|
|
949
|
-
if (world.rules.length === 0) {
|
|
950
|
-
lines.push(' (none)');
|
|
951
|
-
}
|
|
952
|
-
for (const rule of world.rules) {
|
|
953
|
-
lines.push(` [${rule.id}] → ${rule.effect.verdict.toUpperCase()}`);
|
|
954
|
-
if (rule.description)
|
|
955
|
-
lines.push(` ${rule.description}`);
|
|
956
|
-
lines.push(` Trigger: ${rule.trigger.field} ${rule.trigger.operator} ${JSON.stringify(rule.trigger.value)}`);
|
|
957
|
-
lines.push('');
|
|
958
|
-
}
|
|
959
|
-
// ── Roles ──
|
|
960
|
-
lines.push(`--- ROLES (${world.roles.length}) ---`);
|
|
961
|
-
lines.push(`Identity-based constraints.\n`);
|
|
962
|
-
if (world.roles.length === 0) {
|
|
963
|
-
lines.push(' (none)');
|
|
964
|
-
}
|
|
965
|
-
for (const role of world.roles) {
|
|
966
|
-
lines.push(` [${role.id}] ${role.name}`);
|
|
967
|
-
if (role.canDo.length > 0)
|
|
968
|
-
lines.push(` Can do: ${role.canDo.join(', ')}`);
|
|
969
|
-
if (role.cannotDo.length > 0)
|
|
970
|
-
lines.push(` Cannot do: ${role.cannotDo.join(', ')}`);
|
|
971
|
-
lines.push(` Requires approval: ${role.requiresApproval ? 'yes' : 'no'}`);
|
|
972
|
-
lines.push('');
|
|
973
|
-
}
|
|
974
|
-
// ── Role Bindings ──
|
|
975
|
-
const bindings = engine.getRoleBindings();
|
|
976
|
-
lines.push(`--- ROLE BINDINGS (${bindings.length}) ---`);
|
|
977
|
-
lines.push(`Agent identity → governance role. Environment-specific.\n`);
|
|
978
|
-
if (bindings.length === 0) {
|
|
979
|
-
lines.push(' (none — roles are defined but no agents are bound)');
|
|
980
|
-
lines.push(' Run /world bind <agentId> <roleId> to assign.');
|
|
981
|
-
}
|
|
982
|
-
for (const binding of bindings) {
|
|
983
|
-
const role = world.roles.find(r => r.id === binding.roleId);
|
|
984
|
-
const roleName = role ? role.name : '(unknown role)';
|
|
985
|
-
lines.push(` ${binding.agentId} → [${binding.roleId}] ${roleName}`);
|
|
986
|
-
lines.push(` Bound by: ${binding.boundBy}`);
|
|
987
|
-
}
|
|
988
|
-
lines.push('');
|
|
989
|
-
lines.push('========================================');
|
|
990
|
-
return { text: lines.join('\n') };
|
|
991
|
-
}
|
|
992
|
-
// ── /world bind <agentId> <roleId> ──
|
|
993
|
-
if (subcommand === 'bind') {
|
|
994
|
-
const [agentId, roleId] = rest;
|
|
995
|
-
if (!agentId || !roleId) {
|
|
996
|
-
return { text: 'Usage: `/world bind <agentId> <roleId>`\n\nExample: `/world bind agent-coder-01 executor`' };
|
|
997
|
-
}
|
|
998
|
-
// Validate roleId exists in the world
|
|
999
|
-
const world = engine.getWorld();
|
|
1000
|
-
if (!world) {
|
|
1001
|
-
return { text: '[NeuroVerseOS] No world loaded. Run /world bootstrap first.' };
|
|
1002
|
-
}
|
|
1003
|
-
const role = world.roles.find(r => r.id === roleId);
|
|
1004
|
-
if (!role) {
|
|
1005
|
-
const available = world.roles.map(r => `\`${r.id}\` (${r.name})`).join(', ');
|
|
1006
|
-
return { text: `[NeuroVerseOS] Role "${roleId}" not found.\n\nAvailable roles: ${available}` };
|
|
1007
|
-
}
|
|
1008
|
-
const meta = engine.getMetaRecord();
|
|
1009
|
-
if (!meta) {
|
|
1010
|
-
return { text: '[NeuroVerseOS] No meta record. Run /world bootstrap first.' };
|
|
1011
|
-
}
|
|
1012
|
-
// Classify severity and apply
|
|
1013
|
-
const severity = engine.setRoleBinding(agentId, roleId, 'human');
|
|
1014
|
-
engine.saveMeta();
|
|
1015
|
-
engine.hydrateRoleMap(roleMap);
|
|
1016
|
-
const severityLabel = {
|
|
1017
|
-
new_binding: 'LOW — new binding',
|
|
1018
|
-
reassignment: 'HIGH — role reassignment',
|
|
1019
|
-
escalation: 'CRITICAL — privilege escalation',
|
|
1020
|
-
de_escalation: 'LOW — privilege de-escalation',
|
|
1021
|
-
removal: 'HIGH — binding removed',
|
|
1022
|
-
};
|
|
1023
|
-
const lines = [
|
|
1024
|
-
`[NeuroVerseOS] Role binding updated.`,
|
|
1025
|
-
` Agent: ${agentId}`,
|
|
1026
|
-
` Role: ${role.id} (${role.name})`,
|
|
1027
|
-
` Severity: ${severityLabel[severity] ?? severity}`,
|
|
1028
|
-
` Can do: ${role.canDo.join(', ') || '(none)'}`,
|
|
1029
|
-
` Cannot do: ${role.cannotDo.join(', ') || '(none)'}`,
|
|
1030
|
-
` Requires approval: ${role.requiresApproval ? 'yes' : 'no'}`,
|
|
1031
|
-
];
|
|
1032
|
-
if (severity === 'escalation') {
|
|
1033
|
-
lines.push('');
|
|
1034
|
-
lines.push(' ⚠ This is a privilege escalation. The agent now has broader permissions.');
|
|
1035
|
-
}
|
|
1036
|
-
return { text: lines.join('\n') };
|
|
1037
|
-
}
|
|
1038
|
-
// ── /world unbind <agentId> ──
|
|
1039
|
-
if (subcommand === 'unbind') {
|
|
1040
|
-
const [agentId] = rest;
|
|
1041
|
-
if (!agentId) {
|
|
1042
|
-
return { text: 'Usage: `/world unbind <agentId>`' };
|
|
1043
|
-
}
|
|
1044
|
-
const removed = engine.removeRoleBinding(agentId);
|
|
1045
|
-
if (!removed) {
|
|
1046
|
-
return { text: `[NeuroVerseOS] No binding found for agent "${agentId}".` };
|
|
1047
|
-
}
|
|
1048
|
-
engine.saveMeta();
|
|
1049
|
-
engine.hydrateRoleMap(roleMap);
|
|
1050
|
-
return {
|
|
1051
|
-
text: [
|
|
1052
|
-
`[NeuroVerseOS] Role binding removed.`,
|
|
1053
|
-
` Agent: ${agentId}`,
|
|
1054
|
-
` Status: unbound (deny-by-default — role constraints will not apply)`,
|
|
1055
|
-
].join('\n'),
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
// ── /world bindings ──
|
|
1059
|
-
if (subcommand === 'bindings') {
|
|
1060
|
-
const bindings = engine.getRoleBindings();
|
|
1061
|
-
if (bindings.length === 0) {
|
|
1062
|
-
return {
|
|
1063
|
-
text: [
|
|
1064
|
-
'[NeuroVerseOS] No role bindings configured.',
|
|
1065
|
-
'',
|
|
1066
|
-
'Agents are unbound. Role constraints are inert.',
|
|
1067
|
-
'Run `/world bind <agentId> <roleId>` to assign roles.',
|
|
1068
|
-
'',
|
|
1069
|
-
'Available roles:',
|
|
1070
|
-
...(engine.getWorld()?.roles.map(r => ` \`${r.id}\` — ${r.name}`) ?? [' (no world loaded)']),
|
|
1071
|
-
].join('\n'),
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
|
-
const lines = [
|
|
1075
|
-
'**Agent Role Bindings**',
|
|
1076
|
-
'',
|
|
1077
|
-
];
|
|
1078
|
-
for (const binding of bindings) {
|
|
1079
|
-
const role = engine.getWorld()?.roles.find(r => r.id === binding.roleId);
|
|
1080
|
-
const roleName = role ? ` (${role.name})` : ' (role not found in world)';
|
|
1081
|
-
const boundDate = new Date(binding.boundAt).toISOString().slice(0, 19);
|
|
1082
|
-
lines.push(` \`${binding.agentId}\` → \`${binding.roleId}\`${roleName}`);
|
|
1083
|
-
lines.push(` Bound by: ${binding.boundBy} at ${boundDate}`);
|
|
1084
|
-
}
|
|
1085
|
-
return { text: lines.join('\n') };
|
|
1086
|
-
}
|
|
1087
|
-
// ── /world propose ──
|
|
1088
|
-
if (subcommand === 'propose') {
|
|
1089
|
-
if (!monitor) {
|
|
1090
|
-
return { text: '[NeuroVerseOS] Drift tracking is disabled. Enable it to generate proposals.' };
|
|
1091
|
-
}
|
|
1092
|
-
const proposals = monitor.generateProposals();
|
|
1093
|
-
if (proposals.length === 0) {
|
|
1094
|
-
return { text: '[NeuroVerseOS] No amendments to propose. Governance is stable.' };
|
|
1095
|
-
}
|
|
1096
|
-
const lines = [];
|
|
1097
|
-
for (const proposal of proposals) {
|
|
1098
|
-
const id = Date.now().toString(36);
|
|
1099
|
-
const proposalPath = (0, path_1.join)(proposalsDir, `${id}.json`);
|
|
1100
|
-
(0, fs_1.writeFileSync)(proposalPath, JSON.stringify({ ...proposal, id, createdAt: Date.now() }, null, 2));
|
|
1101
|
-
lines.push(`\nProposal ${id}:`);
|
|
1102
|
-
lines.push(` Type: ${proposal.type}`);
|
|
1103
|
-
lines.push(` Reason: ${proposal.reason}`);
|
|
1104
|
-
lines.push(` Suggestion: ${proposal.suggestion}`);
|
|
1105
|
-
}
|
|
1106
|
-
lines.push(`\nRun '/world approve <id>' to apply a proposal.`);
|
|
1107
|
-
return { text: lines.join('\n') };
|
|
1108
|
-
}
|
|
1109
|
-
// ── /world approve <id> ──
|
|
1110
|
-
if (subcommand === 'approve') {
|
|
1111
|
-
const id = rest[0];
|
|
1112
|
-
if (!id) {
|
|
1113
|
-
return { text: 'Usage: /world approve <proposal-id>' };
|
|
1114
|
-
}
|
|
1115
|
-
const proposalPath = (0, path_1.join)(proposalsDir, `${id}.json`);
|
|
1116
|
-
if (!(0, fs_1.existsSync)(proposalPath)) {
|
|
1117
|
-
return { text: `[NeuroVerseOS] Proposal ${id} not found.` };
|
|
1118
|
-
}
|
|
1119
|
-
const proposal = JSON.parse((0, fs_1.readFileSync)(proposalPath, 'utf-8'));
|
|
1120
|
-
engine.applyAmendment(proposal);
|
|
1121
|
-
engine.saveWorld();
|
|
1122
|
-
return { text: `Proposal ${id} approved and applied to world.\n World file updated: ${worldPath}` };
|
|
1123
|
-
}
|
|
1124
|
-
// ── /world export ──
|
|
1125
|
-
if (subcommand === 'export') {
|
|
1126
|
-
if (!(0, fs_1.existsSync)(worldPath)) {
|
|
1127
|
-
return { text: '[NeuroVerseOS] No world file. Run /world bootstrap first.' };
|
|
1128
|
-
}
|
|
1129
|
-
return {
|
|
1130
|
-
text: [
|
|
1131
|
-
'[NeuroVerseOS] Export to .nv-world.zip:',
|
|
1132
|
-
' Use the NeuroVerse configurator at https://www.neuroverseos.com/build/configurator',
|
|
1133
|
-
` Or copy ${worldPath} to use with the Action Space runner.`,
|
|
1134
|
-
].join('\n'),
|
|
1135
|
-
};
|
|
1136
|
-
}
|
|
1137
|
-
return { text: `Unknown subcommand: ${subcommand}. Run /world help for usage.` };
|
|
1138
|
-
},
|
|
1139
|
-
});
|
|
1140
|
-
// ── Startup ────────────────────────────────────────────────────
|
|
1141
|
-
api.logger.info('[NeuroVerseOS] Governance plugin loaded (v1.3.0)');
|
|
1142
|
-
if ((0, fs_1.existsSync)(worldPath)) {
|
|
1143
|
-
engine.loadWorld();
|
|
1144
|
-
engine.loadMeta();
|
|
1145
|
-
const status = engine.getStatus();
|
|
1146
|
-
const ruleCount = status.invariantCount + status.guardCount + status.ruleCount;
|
|
1147
|
-
api.logger.info(`[NeuroVerseOS] World active: ${ruleCount} rules, ${status.roleCount} roles, ${status.enforcement} enforcement`);
|
|
1148
|
-
const meta = engine.getMetaRecord();
|
|
1149
|
-
if (meta) {
|
|
1150
|
-
api.logger.info(`[NeuroVerseOS] Integrity: verified (v${meta.version})`);
|
|
1151
|
-
}
|
|
1152
|
-
// Hydrate roleMap from meta bindings (agent identity → governance role)
|
|
1153
|
-
engine.hydrateRoleMap(roleMap);
|
|
1154
|
-
const bindingCount = engine.getRoleBindings().length;
|
|
1155
|
-
if (bindingCount > 0) {
|
|
1156
|
-
api.logger.info(`[NeuroVerseOS] ${bindingCount} agent(s) bound to roles`);
|
|
1157
|
-
}
|
|
1158
|
-
api.logger.info(`[NeuroVerseOS] Type /world for status, /world help for commands`);
|
|
164
|
+
function evaluateCondition(condition, event) {
|
|
165
|
+
const fieldValue = getFieldValue(event, condition.field);
|
|
166
|
+
if (fieldValue === void 0 && condition.operator !== "!=") {
|
|
167
|
+
return { matched: false, evidence: null };
|
|
168
|
+
}
|
|
169
|
+
switch (condition.operator) {
|
|
170
|
+
case "==":
|
|
171
|
+
return evaluateEquals(fieldValue, condition.value);
|
|
172
|
+
case "!=":
|
|
173
|
+
return evaluateNotEquals(fieldValue, condition.value);
|
|
174
|
+
case ">":
|
|
175
|
+
return evaluateComparison(fieldValue, condition.value, (a, b) => a > b);
|
|
176
|
+
case "<":
|
|
177
|
+
return evaluateComparison(fieldValue, condition.value, (a, b) => a < b);
|
|
178
|
+
case ">=":
|
|
179
|
+
return evaluateComparison(fieldValue, condition.value, (a, b) => a >= b);
|
|
180
|
+
case "<=":
|
|
181
|
+
return evaluateComparison(fieldValue, condition.value, (a, b) => a <= b);
|
|
182
|
+
case "in":
|
|
183
|
+
return evaluateIn(fieldValue, condition.value);
|
|
184
|
+
case "contains":
|
|
185
|
+
return evaluateContains(fieldValue, condition.value);
|
|
186
|
+
case "contains_any":
|
|
187
|
+
return evaluateContainsAny(fieldValue, condition.value);
|
|
188
|
+
case "matches_pattern":
|
|
189
|
+
return evaluateMatchesPattern(fieldValue, condition.value);
|
|
190
|
+
case "starts_with":
|
|
191
|
+
return evaluateStartsWith(fieldValue, condition.value);
|
|
192
|
+
case "ends_with":
|
|
193
|
+
return evaluateEndsWith(fieldValue, condition.value);
|
|
194
|
+
default:
|
|
195
|
+
return { matched: false, evidence: null };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function evaluateEquals(fieldValue, conditionValue) {
|
|
199
|
+
const fieldStr = toString(fieldValue);
|
|
200
|
+
const condStr = toString(conditionValue);
|
|
201
|
+
const matched = fieldStr === condStr;
|
|
202
|
+
return {
|
|
203
|
+
matched,
|
|
204
|
+
evidence: matched ? `${fieldStr} == ${condStr}` : null
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function evaluateNotEquals(fieldValue, conditionValue) {
|
|
208
|
+
const fieldStr = toString(fieldValue);
|
|
209
|
+
const condStr = toString(conditionValue);
|
|
210
|
+
const matched = fieldStr !== condStr;
|
|
211
|
+
return {
|
|
212
|
+
matched,
|
|
213
|
+
evidence: matched ? `${fieldStr} != ${condStr}` : null
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function evaluateComparison(fieldValue, conditionValue, comparator) {
|
|
217
|
+
const a = Number(fieldValue);
|
|
218
|
+
const b = Number(conditionValue);
|
|
219
|
+
if (isNaN(a) || isNaN(b)) return { matched: false, evidence: null };
|
|
220
|
+
const matched = comparator(a, b);
|
|
221
|
+
return {
|
|
222
|
+
matched,
|
|
223
|
+
evidence: matched ? `${a} compared to ${b}` : null
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function evaluateIn(fieldValue, conditionValue) {
|
|
227
|
+
if (!Array.isArray(conditionValue)) return { matched: false, evidence: null };
|
|
228
|
+
const fieldStr = toString(fieldValue);
|
|
229
|
+
const matched = conditionValue.some((v) => toString(v) === fieldStr);
|
|
230
|
+
return {
|
|
231
|
+
matched,
|
|
232
|
+
evidence: matched ? `"${fieldStr}" found in [${conditionValue.join(", ")}]` : null
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function evaluateContains(fieldValue, conditionValue) {
|
|
236
|
+
const fieldStr = toString(fieldValue).toLowerCase();
|
|
237
|
+
const searchStr = toString(conditionValue).toLowerCase();
|
|
238
|
+
const matched = fieldStr.includes(searchStr);
|
|
239
|
+
return {
|
|
240
|
+
matched,
|
|
241
|
+
evidence: matched ? `"${searchStr}" found in field` : null
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function evaluateContainsAny(fieldValue, conditionValue) {
|
|
245
|
+
if (!Array.isArray(conditionValue)) return { matched: false, evidence: null };
|
|
246
|
+
const fieldStr = toString(fieldValue).toLowerCase();
|
|
247
|
+
for (const pattern of conditionValue) {
|
|
248
|
+
const patternLower = toString(pattern).toLowerCase();
|
|
249
|
+
if (fieldStr.includes(patternLower)) {
|
|
250
|
+
return {
|
|
251
|
+
matched: true,
|
|
252
|
+
evidence: `"${patternLower}" found in field`
|
|
253
|
+
};
|
|
1159
254
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
255
|
+
}
|
|
256
|
+
return { matched: false, evidence: null };
|
|
257
|
+
}
|
|
258
|
+
function evaluateMatchesPattern(fieldValue, conditionValue) {
|
|
259
|
+
const fieldStr = toString(fieldValue);
|
|
260
|
+
const patterns = Array.isArray(conditionValue) ? conditionValue : [toString(conditionValue)];
|
|
261
|
+
for (const pattern of patterns) {
|
|
262
|
+
try {
|
|
263
|
+
const regex = new RegExp(toString(pattern), "i");
|
|
264
|
+
if (regex.test(fieldStr)) {
|
|
265
|
+
return {
|
|
266
|
+
matched: true,
|
|
267
|
+
evidence: `matched pattern /${pattern}/`
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
} catch {
|
|
271
|
+
continue;
|
|
1168
272
|
}
|
|
273
|
+
}
|
|
274
|
+
return { matched: false, evidence: null };
|
|
275
|
+
}
|
|
276
|
+
function evaluateStartsWith(fieldValue, conditionValue) {
|
|
277
|
+
const fieldStr = toString(fieldValue).toLowerCase();
|
|
278
|
+
const prefix = toString(conditionValue).toLowerCase();
|
|
279
|
+
const matched = fieldStr.startsWith(prefix);
|
|
280
|
+
return {
|
|
281
|
+
matched,
|
|
282
|
+
evidence: matched ? `field starts with "${prefix}"` : null
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function evaluateEndsWith(fieldValue, conditionValue) {
|
|
286
|
+
const fieldStr = toString(fieldValue).toLowerCase();
|
|
287
|
+
const suffix = toString(conditionValue).toLowerCase();
|
|
288
|
+
const matched = fieldStr.endsWith(suffix);
|
|
289
|
+
return {
|
|
290
|
+
matched,
|
|
291
|
+
evidence: matched ? `field ends with "${suffix}"` : null
|
|
292
|
+
};
|
|
1169
293
|
}
|
|
1170
|
-
|
|
294
|
+
export {
|
|
295
|
+
BOOTSTRAP_EXIT_CODES,
|
|
296
|
+
CONFIGURE_AI_EXIT_CODES,
|
|
297
|
+
CompositeAuditLogger,
|
|
298
|
+
ConsoleAuditLogger,
|
|
299
|
+
DERIVE_EXIT_CODES,
|
|
300
|
+
FileAuditLogger,
|
|
301
|
+
GUARD_EXIT_CODES,
|
|
302
|
+
VALIDATE_EXIT_CODES,
|
|
303
|
+
createGovernanceEngine,
|
|
304
|
+
deriveWorld,
|
|
305
|
+
emitWorldDefinition,
|
|
306
|
+
evaluateCondition,
|
|
307
|
+
evaluateGuard,
|
|
308
|
+
eventToAllowlistKey,
|
|
309
|
+
explainWorld,
|
|
310
|
+
extractWorldMarkdown,
|
|
311
|
+
formatVerdict,
|
|
312
|
+
formatVerdictOneLine,
|
|
313
|
+
generateImpactReport,
|
|
314
|
+
generateImpactReportFromFile,
|
|
315
|
+
improveWorld,
|
|
316
|
+
loadWorld,
|
|
317
|
+
loadWorldFromDirectory,
|
|
318
|
+
normalizeWorldMarkdown,
|
|
319
|
+
parseWorldMarkdown,
|
|
320
|
+
readAuditLog,
|
|
321
|
+
renderExplainText,
|
|
322
|
+
renderImpactReport,
|
|
323
|
+
renderImproveText,
|
|
324
|
+
renderSimulateText,
|
|
325
|
+
simulateWorld,
|
|
326
|
+
summarizeAuditEvents,
|
|
327
|
+
validateWorld,
|
|
328
|
+
verdictToAuditEvent
|
|
329
|
+
};
|