@neuroverseos/governance 0.1.5 → 0.2.1
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/README.md +279 -423
- package/dist/adapters/express.cjs +242 -2
- package/dist/adapters/express.d.cts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +5 -3
- package/dist/adapters/index.cjs +301 -5
- package/dist/adapters/index.d.cts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +8 -6
- package/dist/adapters/langchain.cjs +267 -3
- package/dist/adapters/langchain.d.cts +8 -1
- package/dist/adapters/langchain.d.ts +8 -1
- package/dist/adapters/langchain.js +5 -3
- package/dist/adapters/openai.cjs +267 -3
- package/dist/adapters/openai.d.cts +8 -1
- package/dist/adapters/openai.d.ts +8 -1
- package/dist/adapters/openai.js +5 -3
- package/dist/adapters/openclaw.cjs +267 -3
- package/dist/adapters/openclaw.d.cts +8 -1
- package/dist/adapters/openclaw.d.ts +8 -1
- package/dist/adapters/openclaw.js +5 -3
- package/dist/{bootstrap-H4HHKQ5G.js → bootstrap-GXVDZNF7.js} +2 -1
- package/dist/{build-73KAVHEY.js → build-P42YFKQV.js} +34 -3
- package/dist/{chunk-FYPYZFV5.js → chunk-2JQJ5U5X.js} +1 -1
- package/dist/chunk-37JG24WH.js +161 -0
- package/dist/chunk-5EDDNJU6.js +321 -0
- package/dist/{chunk-O5OMJMIE.js → chunk-7P3S7MAY.js} +502 -2
- package/dist/chunk-A5W4GNQO.js +130 -0
- package/dist/{chunk-ITJ3LCPG.js → chunk-ADV7Q2LJ.js} +1 -1
- package/dist/chunk-AKW5YVCE.js +96 -0
- package/dist/{chunk-EIUHJXBB.js → chunk-GR6DGCZ2.js} +1 -1
- package/dist/{chunk-EQXFOKH2.js → chunk-IVPKFJX3.js} +24 -3
- package/dist/{chunk-D7BGWV2J.js → chunk-NF5POFCI.js} +5 -3
- package/dist/chunk-OT6PXH54.js +61 -0
- package/dist/chunk-P74Y66ZV.js +205 -0
- package/dist/chunk-PAX2P6ZP.js +601 -0
- package/dist/{chunk-B4NF3OLW.js → chunk-PQBJBVSW.js} +56 -2
- package/dist/{chunk-T4X42QXC.js → chunk-Q6O7ZLO2.js} +0 -59
- package/dist/{chunk-FZQCRGUU.js → chunk-TINSRYXQ.js} +24 -3
- package/dist/{chunk-CROPZ75A.js → chunk-UPJNTSVM.js} +24 -3
- package/dist/chunk-YZFATT7X.js +9 -0
- package/dist/{chunk-Z2S2HIV5.js → chunk-ZL4AHY4X.js} +2 -2
- package/dist/cli/neuroverse.cjs +5287 -740
- package/dist/cli/neuroverse.js +69 -13
- package/dist/cli/plan.cjs +1554 -0
- package/dist/cli/plan.d.cts +20 -0
- package/dist/cli/plan.d.ts +20 -0
- package/dist/cli/plan.js +346 -0
- package/dist/cli/run.cjs +1716 -0
- package/dist/cli/run.d.cts +20 -0
- package/dist/cli/run.d.ts +20 -0
- package/dist/cli/run.js +143 -0
- package/dist/{configure-ai-46JVG56I.js → configure-ai-TK67ZWZL.js} +5 -2
- package/dist/{derive-6NAEWLM5.js → derive-TLIV4OOU.js} +6 -4
- package/dist/doctor-V72UM2TC.js +170 -0
- package/dist/{explain-3B3VB6TL.js → explain-IDCRWMPX.js} +2 -1
- package/dist/{guard-67Y66P3I.js → guard-WA3FCCIO.js} +20 -6
- package/dist/{guard-contract-D_RQz9kt.d.ts → guard-contract-D-2LQInm.d.cts} +144 -2
- package/dist/{guard-contract-D_RQz9kt.d.cts → guard-contract-D-2LQInm.d.ts} +144 -2
- package/dist/guard-engine-D7X4CVAE.js +10 -0
- package/dist/{impact-CHERK3O6.js → impact-BWULZ5RP.js} +5 -3
- package/dist/{improve-YG6I6ERG.js → improve-GPUBKTEA.js} +4 -3
- package/dist/index.cjs +2095 -89
- package/dist/index.d.cts +466 -12
- package/dist/index.d.ts +466 -12
- package/dist/index.js +70 -20
- package/dist/{init-Z66T6TDI.js → init-PKPIYHYE.js} +2 -0
- package/dist/mcp-server-YUOQP4M5.js +13 -0
- package/dist/model-adapter-BB7G4MFI.js +11 -0
- package/dist/playground-CBXMAW2B.js +550 -0
- package/dist/redteam-SSNABQ7W.js +357 -0
- package/dist/session-MWRBTCYX.js +14 -0
- package/dist/{simulate-ETHHINZ4.js → simulate-VDOYQFRO.js} +2 -1
- package/dist/test-3GZSG5FR.js +217 -0
- package/dist/{trace-3YODSSIP.js → trace-TM4Z7G73.js} +4 -2
- package/dist/{validate-UVE6GKQU.js → validate-LLBWVPGV.js} +15 -6
- package/dist/validate-engine-UIABSIHD.js +7 -0
- package/dist/{world-WLNHL5XC.js → world-LAXO6DOX.js} +87 -7
- package/dist/world-loader-HMPTOEA2.js +9 -0
- package/package.json +19 -5
- package/dist/validate-engine-657D75OG.js +0 -6
- /package/dist/{chunk-M3TZFGHO.js → chunk-JZPQGIKR.js} +0 -0
|
@@ -37,6 +37,205 @@ __export(openclaw_exports, {
|
|
|
37
37
|
});
|
|
38
38
|
module.exports = __toCommonJS(openclaw_exports);
|
|
39
39
|
|
|
40
|
+
// src/engine/plan-engine.ts
|
|
41
|
+
function keywordMatch(eventText, step) {
|
|
42
|
+
const stepText = [
|
|
43
|
+
step.label,
|
|
44
|
+
step.description ?? "",
|
|
45
|
+
...step.tags ?? []
|
|
46
|
+
].join(" ").toLowerCase();
|
|
47
|
+
const keywords = stepText.split(/\s+/).filter((w) => w.length > 3);
|
|
48
|
+
if (keywords.length === 0) return false;
|
|
49
|
+
const matched = keywords.filter((kw) => eventText.includes(kw));
|
|
50
|
+
return matched.length >= Math.ceil(keywords.length * 0.5);
|
|
51
|
+
}
|
|
52
|
+
function tokenSimilarity(a, b) {
|
|
53
|
+
const tokensA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
54
|
+
const tokensB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
55
|
+
if (tokensA.size === 0 || tokensB.size === 0) return 0;
|
|
56
|
+
let intersection = 0;
|
|
57
|
+
for (const t of tokensA) {
|
|
58
|
+
if (tokensB.has(t)) intersection++;
|
|
59
|
+
}
|
|
60
|
+
const union = (/* @__PURE__ */ new Set([...tokensA, ...tokensB])).size;
|
|
61
|
+
return union > 0 ? intersection / union : 0;
|
|
62
|
+
}
|
|
63
|
+
function findMatchingStep(eventText, event, steps) {
|
|
64
|
+
const pendingOrActive = steps.filter((s) => s.status === "pending" || s.status === "active");
|
|
65
|
+
if (pendingOrActive.length === 0) {
|
|
66
|
+
return { matched: null, closest: null, closestScore: 0 };
|
|
67
|
+
}
|
|
68
|
+
for (const step of pendingOrActive) {
|
|
69
|
+
if (keywordMatch(eventText, step)) {
|
|
70
|
+
if (step.tools && event.tool && !step.tools.includes(event.tool)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
return { matched: step, closest: step, closestScore: 1 };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const intentText = [event.intent, event.tool ?? "", event.scope ?? ""].join(" ");
|
|
77
|
+
let bestStep = null;
|
|
78
|
+
let bestScore = 0;
|
|
79
|
+
for (const step of pendingOrActive) {
|
|
80
|
+
const stepText = [step.label, step.description ?? "", ...step.tags ?? []].join(" ");
|
|
81
|
+
const score = tokenSimilarity(intentText, stepText);
|
|
82
|
+
if (score > bestScore) {
|
|
83
|
+
bestScore = score;
|
|
84
|
+
bestStep = step;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const SIMILARITY_THRESHOLD = 0.35;
|
|
88
|
+
if (bestScore >= SIMILARITY_THRESHOLD && bestStep) {
|
|
89
|
+
if (bestStep.tools && event.tool && !bestStep.tools.includes(event.tool)) {
|
|
90
|
+
return { matched: null, closest: bestStep, closestScore: bestScore };
|
|
91
|
+
}
|
|
92
|
+
return { matched: bestStep, closest: bestStep, closestScore: bestScore };
|
|
93
|
+
}
|
|
94
|
+
return { matched: null, closest: bestStep, closestScore: bestScore };
|
|
95
|
+
}
|
|
96
|
+
function isSequenceValid(step, plan) {
|
|
97
|
+
if (!plan.sequential) return true;
|
|
98
|
+
if (!step.requires || step.requires.length === 0) return true;
|
|
99
|
+
return step.requires.every((reqId) => {
|
|
100
|
+
const reqStep = plan.steps.find((s) => s.id === reqId);
|
|
101
|
+
return reqStep?.status === "completed";
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function checkConstraints(event, eventText, constraints) {
|
|
105
|
+
const checks = [];
|
|
106
|
+
for (const constraint of constraints) {
|
|
107
|
+
if (constraint.type === "approval") {
|
|
108
|
+
if (constraint.trigger && eventText.includes(constraint.trigger.substring(0, 10).toLowerCase())) {
|
|
109
|
+
checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
|
|
110
|
+
return { violated: constraint, checks };
|
|
111
|
+
}
|
|
112
|
+
const keywords = constraint.description.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
|
|
113
|
+
const relevant = keywords.some((kw) => eventText.includes(kw));
|
|
114
|
+
if (relevant) {
|
|
115
|
+
checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
|
|
116
|
+
return { violated: constraint, checks };
|
|
117
|
+
}
|
|
118
|
+
checks.push({ constraintId: constraint.id, passed: true });
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (constraint.type === "scope" && constraint.trigger) {
|
|
122
|
+
const keywords = constraint.trigger.split(/\s+/).filter((w) => w.length > 3);
|
|
123
|
+
const violated = keywords.length > 0 && keywords.every((kw) => eventText.includes(kw));
|
|
124
|
+
checks.push({
|
|
125
|
+
constraintId: constraint.id,
|
|
126
|
+
passed: !violated,
|
|
127
|
+
reason: violated ? constraint.description : void 0
|
|
128
|
+
});
|
|
129
|
+
if (violated) {
|
|
130
|
+
return { violated: constraint, checks };
|
|
131
|
+
}
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
checks.push({ constraintId: constraint.id, passed: true });
|
|
135
|
+
}
|
|
136
|
+
return { violated: null, checks };
|
|
137
|
+
}
|
|
138
|
+
function getPlanProgress(plan) {
|
|
139
|
+
const completed = plan.steps.filter((s) => s.status === "completed").length;
|
|
140
|
+
const total = plan.steps.length;
|
|
141
|
+
return {
|
|
142
|
+
completed,
|
|
143
|
+
total,
|
|
144
|
+
percentage: total > 0 ? Math.round(completed / total * 100) : 0
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function advancePlan(plan, stepId) {
|
|
148
|
+
return {
|
|
149
|
+
...plan,
|
|
150
|
+
steps: plan.steps.map(
|
|
151
|
+
(s) => s.id === stepId ? { ...s, status: "completed" } : s
|
|
152
|
+
)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function evaluatePlan(event, plan) {
|
|
156
|
+
const progress = getPlanProgress(plan);
|
|
157
|
+
if (plan.expires_at) {
|
|
158
|
+
const expiresAt = new Date(plan.expires_at).getTime();
|
|
159
|
+
if (Date.now() > expiresAt) {
|
|
160
|
+
return {
|
|
161
|
+
allowed: true,
|
|
162
|
+
status: "PLAN_COMPLETE",
|
|
163
|
+
reason: "Plan has expired.",
|
|
164
|
+
progress
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (progress.completed === progress.total) {
|
|
169
|
+
return {
|
|
170
|
+
allowed: true,
|
|
171
|
+
status: "PLAN_COMPLETE",
|
|
172
|
+
reason: "All plan steps are completed.",
|
|
173
|
+
progress
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const eventText = [
|
|
177
|
+
event.intent,
|
|
178
|
+
event.tool ?? "",
|
|
179
|
+
event.scope ?? ""
|
|
180
|
+
].join(" ").toLowerCase();
|
|
181
|
+
const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
|
|
182
|
+
if (!matched) {
|
|
183
|
+
return {
|
|
184
|
+
allowed: false,
|
|
185
|
+
status: "OFF_PLAN",
|
|
186
|
+
reason: "Action does not match any plan step.",
|
|
187
|
+
closestStep: closest?.label,
|
|
188
|
+
similarityScore: closestScore,
|
|
189
|
+
progress
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (!isSequenceValid(matched, plan)) {
|
|
193
|
+
const pendingDeps = (matched.requires ?? []).filter((reqId) => plan.steps.find((s) => s.id === reqId)?.status !== "completed").join(", ");
|
|
194
|
+
return {
|
|
195
|
+
allowed: false,
|
|
196
|
+
status: "OFF_PLAN",
|
|
197
|
+
reason: `Step "${matched.label}" requires completion of: ${pendingDeps}`,
|
|
198
|
+
matchedStep: matched.id,
|
|
199
|
+
progress
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
const { violated } = checkConstraints(event, eventText, plan.constraints);
|
|
203
|
+
if (violated) {
|
|
204
|
+
return {
|
|
205
|
+
allowed: false,
|
|
206
|
+
status: "CONSTRAINT_VIOLATED",
|
|
207
|
+
reason: violated.description,
|
|
208
|
+
matchedStep: matched.id,
|
|
209
|
+
progress
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
allowed: true,
|
|
214
|
+
status: "ON_PLAN",
|
|
215
|
+
reason: `Matches step: ${matched.label}`,
|
|
216
|
+
matchedStep: matched.id,
|
|
217
|
+
progress
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function buildPlanCheck(event, plan, verdict) {
|
|
221
|
+
const eventText = [event.intent, event.tool ?? "", event.scope ?? ""].join(" ").toLowerCase();
|
|
222
|
+
const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
|
|
223
|
+
const { checks: constraintChecks } = checkConstraints(event, eventText, plan.constraints);
|
|
224
|
+
const progress = getPlanProgress(plan);
|
|
225
|
+
return {
|
|
226
|
+
planId: plan.plan_id,
|
|
227
|
+
matched: !!matched,
|
|
228
|
+
matchedStepId: matched?.id,
|
|
229
|
+
matchedStepLabel: matched?.label,
|
|
230
|
+
closestStepId: !matched ? closest?.id : void 0,
|
|
231
|
+
closestStepLabel: !matched ? closest?.label : void 0,
|
|
232
|
+
similarityScore: !matched ? closestScore : void 0,
|
|
233
|
+
sequenceValid: matched ? isSequenceValid(matched, plan) : void 0,
|
|
234
|
+
constraintsChecked: constraintChecks,
|
|
235
|
+
progress: { completed: progress.completed, total: progress.total }
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
40
239
|
// src/engine/guard-engine.ts
|
|
41
240
|
var PROMPT_INJECTION_PATTERNS = [
|
|
42
241
|
// Instruction override
|
|
@@ -128,6 +327,7 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
128
327
|
const eventText = (event.intent + " " + (event.tool ?? "") + " " + (event.scope ?? "")).toLowerCase();
|
|
129
328
|
const invariantChecks = [];
|
|
130
329
|
const safetyChecks = [];
|
|
330
|
+
let planCheckResult;
|
|
131
331
|
const roleChecks = [];
|
|
132
332
|
const guardChecks = [];
|
|
133
333
|
const kernelRuleChecks = [];
|
|
@@ -155,6 +355,7 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
155
355
|
includeTrace ? buildTrace(
|
|
156
356
|
invariantChecks,
|
|
157
357
|
safetyChecks,
|
|
358
|
+
planCheckResult,
|
|
158
359
|
roleChecks,
|
|
159
360
|
guardChecks,
|
|
160
361
|
kernelRuleChecks,
|
|
@@ -183,6 +384,7 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
183
384
|
includeTrace ? buildTrace(
|
|
184
385
|
invariantChecks,
|
|
185
386
|
safetyChecks,
|
|
387
|
+
planCheckResult,
|
|
186
388
|
roleChecks,
|
|
187
389
|
guardChecks,
|
|
188
390
|
kernelRuleChecks,
|
|
@@ -193,6 +395,42 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
193
395
|
) : void 0
|
|
194
396
|
);
|
|
195
397
|
}
|
|
398
|
+
if (options.plan) {
|
|
399
|
+
const planVerdict = evaluatePlan(event, options.plan);
|
|
400
|
+
planCheckResult = buildPlanCheck(event, options.plan, planVerdict);
|
|
401
|
+
if (!planVerdict.allowed && planVerdict.status !== "PLAN_COMPLETE") {
|
|
402
|
+
decidingLayer = "plan-enforcement";
|
|
403
|
+
decidingId = `plan-${options.plan.plan_id}`;
|
|
404
|
+
const planStatus = planVerdict.status === "CONSTRAINT_VIOLATED" ? "PAUSE" : "BLOCK";
|
|
405
|
+
let reason = planVerdict.reason ?? "Action blocked by plan.";
|
|
406
|
+
if (planVerdict.status === "OFF_PLAN" && planVerdict.closestStep) {
|
|
407
|
+
reason += ` Closest step: "${planVerdict.closestStep}" (similarity: ${(planVerdict.similarityScore ?? 0).toFixed(2)})`;
|
|
408
|
+
}
|
|
409
|
+
return buildVerdict(
|
|
410
|
+
planStatus,
|
|
411
|
+
reason,
|
|
412
|
+
`plan-${options.plan.plan_id}`,
|
|
413
|
+
void 0,
|
|
414
|
+
world,
|
|
415
|
+
level,
|
|
416
|
+
invariantChecks,
|
|
417
|
+
guardsMatched,
|
|
418
|
+
rulesMatched,
|
|
419
|
+
includeTrace ? buildTrace(
|
|
420
|
+
invariantChecks,
|
|
421
|
+
safetyChecks,
|
|
422
|
+
planCheckResult,
|
|
423
|
+
roleChecks,
|
|
424
|
+
guardChecks,
|
|
425
|
+
kernelRuleChecks,
|
|
426
|
+
levelChecks,
|
|
427
|
+
decidingLayer,
|
|
428
|
+
decidingId,
|
|
429
|
+
startTime
|
|
430
|
+
) : void 0
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
196
434
|
const roleVerdict = checkRoleRules(event, eventText, world, roleChecks);
|
|
197
435
|
if (roleVerdict) {
|
|
198
436
|
decidingLayer = "role";
|
|
@@ -210,6 +448,7 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
210
448
|
includeTrace ? buildTrace(
|
|
211
449
|
invariantChecks,
|
|
212
450
|
safetyChecks,
|
|
451
|
+
planCheckResult,
|
|
213
452
|
roleChecks,
|
|
214
453
|
guardChecks,
|
|
215
454
|
kernelRuleChecks,
|
|
@@ -238,6 +477,7 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
238
477
|
includeTrace ? buildTrace(
|
|
239
478
|
invariantChecks,
|
|
240
479
|
safetyChecks,
|
|
480
|
+
planCheckResult,
|
|
241
481
|
roleChecks,
|
|
242
482
|
guardChecks,
|
|
243
483
|
kernelRuleChecks,
|
|
@@ -266,6 +506,7 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
266
506
|
includeTrace ? buildTrace(
|
|
267
507
|
invariantChecks,
|
|
268
508
|
safetyChecks,
|
|
509
|
+
planCheckResult,
|
|
269
510
|
roleChecks,
|
|
270
511
|
guardChecks,
|
|
271
512
|
kernelRuleChecks,
|
|
@@ -293,6 +534,7 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
293
534
|
includeTrace ? buildTrace(
|
|
294
535
|
invariantChecks,
|
|
295
536
|
safetyChecks,
|
|
537
|
+
planCheckResult,
|
|
296
538
|
roleChecks,
|
|
297
539
|
guardChecks,
|
|
298
540
|
kernelRuleChecks,
|
|
@@ -317,6 +559,7 @@ function evaluateGuard(event, world, options = {}) {
|
|
|
317
559
|
includeTrace ? buildTrace(
|
|
318
560
|
invariantChecks,
|
|
319
561
|
safetyChecks,
|
|
562
|
+
planCheckResult,
|
|
320
563
|
roleChecks,
|
|
321
564
|
guardChecks,
|
|
322
565
|
kernelRuleChecks,
|
|
@@ -654,8 +897,8 @@ function matchesKeywords(eventText, ruleText) {
|
|
|
654
897
|
function eventToAllowlistKey(event) {
|
|
655
898
|
return `${(event.tool ?? "*").toLowerCase()}::${event.intent.toLowerCase().trim()}`;
|
|
656
899
|
}
|
|
657
|
-
function buildTrace(invariantChecks, safetyChecks, roleChecks, guardChecks, kernelRuleChecks, levelChecks, decidingLayer, decidingId, startTime) {
|
|
658
|
-
|
|
900
|
+
function buildTrace(invariantChecks, safetyChecks, planCheck, roleChecks, guardChecks, kernelRuleChecks, levelChecks, decidingLayer, decidingId, startTime) {
|
|
901
|
+
const trace = {
|
|
659
902
|
invariantChecks,
|
|
660
903
|
safetyChecks,
|
|
661
904
|
roleChecks,
|
|
@@ -673,6 +916,7 @@ function buildTrace(invariantChecks, safetyChecks, roleChecks, guardChecks, kern
|
|
|
673
916
|
"safety-scope-escape",
|
|
674
917
|
"safety-execution-claim",
|
|
675
918
|
"safety-execution-intent",
|
|
919
|
+
"plan-enforcement",
|
|
676
920
|
"role-rules",
|
|
677
921
|
"declarative-guards",
|
|
678
922
|
"kernel-rules",
|
|
@@ -682,6 +926,10 @@ function buildTrace(invariantChecks, safetyChecks, roleChecks, guardChecks, kern
|
|
|
682
926
|
},
|
|
683
927
|
durationMs: performance.now() - startTime
|
|
684
928
|
};
|
|
929
|
+
if (planCheck) {
|
|
930
|
+
trace.planCheck = planCheck;
|
|
931
|
+
}
|
|
932
|
+
return trace;
|
|
685
933
|
}
|
|
686
934
|
function buildVerdict(status, reason, ruleId, warning, world, level, invariantChecks, guardsMatched, rulesMatched, trace) {
|
|
687
935
|
const evidence = {
|
|
@@ -807,12 +1055,15 @@ var NeuroVersePlugin = class {
|
|
|
807
1055
|
options;
|
|
808
1056
|
engineOptions;
|
|
809
1057
|
mapAction;
|
|
1058
|
+
activePlan;
|
|
810
1059
|
constructor(world, options = {}) {
|
|
811
1060
|
this.world = world;
|
|
812
1061
|
this.options = options;
|
|
1062
|
+
this.activePlan = options.plan;
|
|
813
1063
|
this.engineOptions = {
|
|
814
1064
|
trace: options.trace ?? false,
|
|
815
|
-
level: options.level
|
|
1065
|
+
level: options.level,
|
|
1066
|
+
plan: this.activePlan
|
|
816
1067
|
};
|
|
817
1068
|
this.mapAction = options.mapAction ?? defaultMapAction;
|
|
818
1069
|
}
|
|
@@ -824,6 +1075,7 @@ var NeuroVersePlugin = class {
|
|
|
824
1075
|
*/
|
|
825
1076
|
beforeAction(action) {
|
|
826
1077
|
const event = this.mapAction(action, "input");
|
|
1078
|
+
this.engineOptions.plan = this.activePlan;
|
|
827
1079
|
const verdict = evaluateGuard(event, this.world, this.engineOptions);
|
|
828
1080
|
const result = {
|
|
829
1081
|
allowed: verdict.status === "ALLOW",
|
|
@@ -834,6 +1086,18 @@ var NeuroVersePlugin = class {
|
|
|
834
1086
|
if (verdict.status === "BLOCK") {
|
|
835
1087
|
throw new GovernanceBlockedError(verdict, action);
|
|
836
1088
|
}
|
|
1089
|
+
if (verdict.status === "ALLOW" && this.activePlan) {
|
|
1090
|
+
const planVerdict = evaluatePlan(event, this.activePlan);
|
|
1091
|
+
if (planVerdict.matchedStep) {
|
|
1092
|
+
this.activePlan = advancePlan(this.activePlan, planVerdict.matchedStep);
|
|
1093
|
+
this.engineOptions.plan = this.activePlan;
|
|
1094
|
+
const progress = getPlanProgress(this.activePlan);
|
|
1095
|
+
this.options.onPlanProgress?.(progress);
|
|
1096
|
+
if (progress.completed === progress.total) {
|
|
1097
|
+
this.options.onPlanComplete?.();
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
837
1101
|
return result;
|
|
838
1102
|
}
|
|
839
1103
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as GuardVerdict, W as WorldDefinition, a as GuardEvent } from '../guard-contract-
|
|
1
|
+
import { G as GuardVerdict, W as WorldDefinition, a as GuardEvent, P as PlanDefinition, b as PlanProgress } from '../guard-contract-D-2LQInm.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* NeuroVerse Adapter — OpenClaw
|
|
@@ -38,6 +38,12 @@ interface NeuroVersePluginOptions {
|
|
|
38
38
|
mapAction?: (action: AgentAction, direction: 'input' | 'output') => GuardEvent;
|
|
39
39
|
/** Whether to evaluate output actions (post-action). Default: false. */
|
|
40
40
|
evaluateOutputs?: boolean;
|
|
41
|
+
/** Active plan overlay for task-scoped governance. */
|
|
42
|
+
plan?: PlanDefinition;
|
|
43
|
+
/** Called when plan progress changes. */
|
|
44
|
+
onPlanProgress?: (progress: PlanProgress) => void;
|
|
45
|
+
/** Called when all plan steps are completed. */
|
|
46
|
+
onPlanComplete?: () => void;
|
|
41
47
|
}
|
|
42
48
|
declare class GovernanceBlockedError extends Error {
|
|
43
49
|
readonly verdict: GuardVerdict;
|
|
@@ -57,6 +63,7 @@ declare class NeuroVersePlugin {
|
|
|
57
63
|
private options;
|
|
58
64
|
private engineOptions;
|
|
59
65
|
private mapAction;
|
|
66
|
+
private activePlan?;
|
|
60
67
|
constructor(world: WorldDefinition, options?: NeuroVersePluginOptions);
|
|
61
68
|
/**
|
|
62
69
|
* Evaluate an action before execution.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as GuardVerdict, W as WorldDefinition, a as GuardEvent } from '../guard-contract-
|
|
1
|
+
import { G as GuardVerdict, W as WorldDefinition, a as GuardEvent, P as PlanDefinition, b as PlanProgress } from '../guard-contract-D-2LQInm.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* NeuroVerse Adapter — OpenClaw
|
|
@@ -38,6 +38,12 @@ interface NeuroVersePluginOptions {
|
|
|
38
38
|
mapAction?: (action: AgentAction, direction: 'input' | 'output') => GuardEvent;
|
|
39
39
|
/** Whether to evaluate output actions (post-action). Default: false. */
|
|
40
40
|
evaluateOutputs?: boolean;
|
|
41
|
+
/** Active plan overlay for task-scoped governance. */
|
|
42
|
+
plan?: PlanDefinition;
|
|
43
|
+
/** Called when plan progress changes. */
|
|
44
|
+
onPlanProgress?: (progress: PlanProgress) => void;
|
|
45
|
+
/** Called when all plan steps are completed. */
|
|
46
|
+
onPlanComplete?: () => void;
|
|
41
47
|
}
|
|
42
48
|
declare class GovernanceBlockedError extends Error {
|
|
43
49
|
readonly verdict: GuardVerdict;
|
|
@@ -57,6 +63,7 @@ declare class NeuroVersePlugin {
|
|
|
57
63
|
private options;
|
|
58
64
|
private engineOptions;
|
|
59
65
|
private mapAction;
|
|
66
|
+
private activePlan?;
|
|
60
67
|
constructor(world: WorldDefinition, options?: NeuroVersePluginOptions);
|
|
61
68
|
/**
|
|
62
69
|
* Evaluate an action before execution.
|
|
@@ -3,9 +3,11 @@ import {
|
|
|
3
3
|
NeuroVersePlugin,
|
|
4
4
|
createNeuroVersePlugin,
|
|
5
5
|
createNeuroVersePluginFromWorld
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-UPJNTSVM.js";
|
|
7
|
+
import "../chunk-PQBJBVSW.js";
|
|
8
|
+
import "../chunk-JZPQGIKR.js";
|
|
9
|
+
import "../chunk-P74Y66ZV.js";
|
|
10
|
+
import "../chunk-YZFATT7X.js";
|
|
9
11
|
export {
|
|
10
12
|
GovernanceBlockedError,
|
|
11
13
|
NeuroVersePlugin,
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
emitWorldDefinition,
|
|
6
6
|
parseWorldMarkdown
|
|
7
7
|
} from "./chunk-XPDMYECO.js";
|
|
8
|
+
import "./chunk-YZFATT7X.js";
|
|
8
9
|
|
|
9
10
|
// src/cli/bootstrap.ts
|
|
10
11
|
function parseArgs(argv) {
|
|
@@ -81,7 +82,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
81
82
|
await writeWorldFiles(args.outputPath, emitResult.world);
|
|
82
83
|
let validateReport;
|
|
83
84
|
if (args.validate) {
|
|
84
|
-
const { validateWorld } = await import("./validate-engine-
|
|
85
|
+
const { validateWorld } = await import("./validate-engine-UIABSIHD.js");
|
|
85
86
|
validateReport = validateWorld(emitResult.world);
|
|
86
87
|
}
|
|
87
88
|
const result = {
|
|
@@ -2,15 +2,17 @@ import {
|
|
|
2
2
|
DeriveInputError,
|
|
3
3
|
DeriveProviderError,
|
|
4
4
|
deriveWorld
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NF5POFCI.js";
|
|
6
6
|
import {
|
|
7
7
|
DERIVE_EXIT_CODES
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-Q6O7ZLO2.js";
|
|
9
|
+
import "./chunk-OT6PXH54.js";
|
|
9
10
|
import {
|
|
10
11
|
emitWorldDefinition,
|
|
11
12
|
parseWorldMarkdown
|
|
12
13
|
} from "./chunk-XPDMYECO.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-7P3S7MAY.js";
|
|
15
|
+
import "./chunk-YZFATT7X.js";
|
|
14
16
|
|
|
15
17
|
// src/cli/build.ts
|
|
16
18
|
var FINDING_LABELS = {
|
|
@@ -74,6 +76,34 @@ function traceCausalChains(rules) {
|
|
|
74
76
|
}
|
|
75
77
|
return unique.slice(0, 3);
|
|
76
78
|
}
|
|
79
|
+
function renderGovernanceHealth(health) {
|
|
80
|
+
const lines = [];
|
|
81
|
+
lines.push("GOVERNANCE HEALTH");
|
|
82
|
+
lines.push(` Coverage: ${health.surfacesCovered} / ${health.surfacesTotal} action surfaces governed`);
|
|
83
|
+
lines.push(` Invariants enforced: ${health.invariantsEnforced} / ${health.invariantsTotal}`);
|
|
84
|
+
if (health.shadowedGuards > 0) {
|
|
85
|
+
lines.push(` Shadowed guards: ${health.shadowedGuards}`);
|
|
86
|
+
}
|
|
87
|
+
if (health.unenforcedInvariants > 0) {
|
|
88
|
+
lines.push(` Unenforced invariants: ${health.unenforcedInvariants}`);
|
|
89
|
+
}
|
|
90
|
+
if (health.unreachableRules > 0) {
|
|
91
|
+
lines.push(` Unreachable rules: ${health.unreachableRules}`);
|
|
92
|
+
}
|
|
93
|
+
if (health.incompleteStateCoverage > 0) {
|
|
94
|
+
lines.push(` Incomplete state coverage: ${health.incompleteStateCoverage}`);
|
|
95
|
+
}
|
|
96
|
+
if (health.surfaces.length > 0) {
|
|
97
|
+
lines.push("");
|
|
98
|
+
lines.push(" Surfaces:");
|
|
99
|
+
for (const s of health.surfaces) {
|
|
100
|
+
lines.push(` ${s.name}: ${s.governed ? "governed" : "unguarded"}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
lines.push("");
|
|
104
|
+
lines.push(` Risk level: ${health.riskLevel.charAt(0).toUpperCase() + health.riskLevel.slice(1)}`);
|
|
105
|
+
return lines.join("\n");
|
|
106
|
+
}
|
|
77
107
|
function parseArgs(argv) {
|
|
78
108
|
let inputPath = "";
|
|
79
109
|
let outputDir;
|
|
@@ -304,5 +334,6 @@ Next steps:
|
|
|
304
334
|
export {
|
|
305
335
|
humanLabel,
|
|
306
336
|
main,
|
|
337
|
+
renderGovernanceHealth,
|
|
307
338
|
traceCausalChains
|
|
308
339
|
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// src/engine/plan-parser.ts
|
|
2
|
+
function slugify(text) {
|
|
3
|
+
return text.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+/g, "_");
|
|
4
|
+
}
|
|
5
|
+
function extractBracketAnnotation(line, key) {
|
|
6
|
+
const regex = new RegExp(`\\[${key}:\\s*([^\\]]+)\\]`, "i");
|
|
7
|
+
const match = line.match(regex);
|
|
8
|
+
if (!match) return null;
|
|
9
|
+
return match[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
10
|
+
}
|
|
11
|
+
function extractParenAnnotation(line, key) {
|
|
12
|
+
const regex = new RegExp(`\\(${key}:\\s*([^)]+)\\)`, "i");
|
|
13
|
+
const match = line.match(regex);
|
|
14
|
+
if (!match) return null;
|
|
15
|
+
return match[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
16
|
+
}
|
|
17
|
+
function stripAnnotations(line) {
|
|
18
|
+
return line.replace(/\[(?:tools|tag|verify|type):\s*[^\]]+\]/gi, "").replace(/\((?:after):\s*[^)]+\)/gi, "").trim();
|
|
19
|
+
}
|
|
20
|
+
function parseFrontmatter(content) {
|
|
21
|
+
const frontmatter = {};
|
|
22
|
+
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
|
|
23
|
+
if (!fmMatch) {
|
|
24
|
+
return { frontmatter, body: content };
|
|
25
|
+
}
|
|
26
|
+
const fmBody = fmMatch[1];
|
|
27
|
+
for (const line of fmBody.split("\n")) {
|
|
28
|
+
const colonIndex = line.indexOf(":");
|
|
29
|
+
if (colonIndex > 0) {
|
|
30
|
+
const key = line.slice(0, colonIndex).trim();
|
|
31
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
32
|
+
frontmatter[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { frontmatter, body: content.slice(fmMatch[0].length) };
|
|
36
|
+
}
|
|
37
|
+
function parseSections(body) {
|
|
38
|
+
const steps = [];
|
|
39
|
+
const constraints = [];
|
|
40
|
+
let currentSection = "none";
|
|
41
|
+
for (const line of body.split("\n")) {
|
|
42
|
+
const trimmed = line.trim();
|
|
43
|
+
if (/^#+\s*Steps/i.test(trimmed)) {
|
|
44
|
+
currentSection = "steps";
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (/^#+\s*Constraints/i.test(trimmed)) {
|
|
48
|
+
currentSection = "constraints";
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (/^#+\s/.test(trimmed) && currentSection !== "none") {
|
|
52
|
+
currentSection = "none";
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (trimmed.startsWith("- ")) {
|
|
56
|
+
const item = trimmed.slice(2).trim();
|
|
57
|
+
if (currentSection === "steps") {
|
|
58
|
+
steps.push(item);
|
|
59
|
+
} else if (currentSection === "constraints") {
|
|
60
|
+
constraints.push(item);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { steps, constraints };
|
|
65
|
+
}
|
|
66
|
+
function parseStep(raw) {
|
|
67
|
+
const label = stripAnnotations(raw);
|
|
68
|
+
const id = slugify(label);
|
|
69
|
+
const tools = extractBracketAnnotation(raw, "tools");
|
|
70
|
+
const tags = extractBracketAnnotation(raw, "tag");
|
|
71
|
+
const verifyArr = extractBracketAnnotation(raw, "verify");
|
|
72
|
+
const requires = extractParenAnnotation(raw, "after");
|
|
73
|
+
return {
|
|
74
|
+
id,
|
|
75
|
+
label,
|
|
76
|
+
tools: tools ?? void 0,
|
|
77
|
+
tags: tags ?? void 0,
|
|
78
|
+
verify: verifyArr?.[0] ?? void 0,
|
|
79
|
+
requires: requires ?? void 0,
|
|
80
|
+
status: "pending"
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function parseConstraint(raw, index) {
|
|
84
|
+
const typeAnnotation = extractBracketAnnotation(raw, "type");
|
|
85
|
+
const description = stripAnnotations(raw);
|
|
86
|
+
const id = `constraint_${index}`;
|
|
87
|
+
let type = "custom";
|
|
88
|
+
let enforcement = "block";
|
|
89
|
+
let limit;
|
|
90
|
+
let unit;
|
|
91
|
+
if (typeAnnotation?.[0] === "approval") {
|
|
92
|
+
type = "approval";
|
|
93
|
+
enforcement = "pause";
|
|
94
|
+
} else if (/budget|\$|spending|cost/i.test(description)) {
|
|
95
|
+
type = "budget";
|
|
96
|
+
const amountMatch = description.match(/\$?([\d,]+)/);
|
|
97
|
+
if (amountMatch) {
|
|
98
|
+
limit = parseInt(amountMatch[1].replace(/,/g, ""), 10);
|
|
99
|
+
unit = "USD";
|
|
100
|
+
}
|
|
101
|
+
} else if (/time|hour|minute|day|deadline/i.test(description)) {
|
|
102
|
+
type = "time";
|
|
103
|
+
} else if (/scope|access|database|production/i.test(description)) {
|
|
104
|
+
type = "scope";
|
|
105
|
+
}
|
|
106
|
+
const trigger = description.toLowerCase();
|
|
107
|
+
return {
|
|
108
|
+
id,
|
|
109
|
+
type,
|
|
110
|
+
description,
|
|
111
|
+
enforcement,
|
|
112
|
+
limit,
|
|
113
|
+
unit,
|
|
114
|
+
trigger
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function parsePlanMarkdown(markdown) {
|
|
118
|
+
const errors = [];
|
|
119
|
+
const { frontmatter, body } = parseFrontmatter(markdown.trim());
|
|
120
|
+
const { steps: stepLines, constraints: constraintLines } = parseSections(body);
|
|
121
|
+
if (!frontmatter.plan_id) {
|
|
122
|
+
errors.push("Missing required field: plan_id");
|
|
123
|
+
}
|
|
124
|
+
if (stepLines.length === 0) {
|
|
125
|
+
errors.push("Plan must have at least one step");
|
|
126
|
+
}
|
|
127
|
+
if (errors.length > 0) {
|
|
128
|
+
return { success: false, errors };
|
|
129
|
+
}
|
|
130
|
+
const steps = stepLines.map((line) => parseStep(line));
|
|
131
|
+
const constraints = constraintLines.map((line, i) => parseConstraint(line, i));
|
|
132
|
+
let expires_at;
|
|
133
|
+
if (frontmatter.expires) {
|
|
134
|
+
expires_at = new Date(frontmatter.expires).toISOString();
|
|
135
|
+
}
|
|
136
|
+
const plan = {
|
|
137
|
+
plan_id: frontmatter.plan_id,
|
|
138
|
+
objective: frontmatter.objective ?? "",
|
|
139
|
+
sequential: frontmatter.sequential === "true",
|
|
140
|
+
steps,
|
|
141
|
+
constraints,
|
|
142
|
+
world_id: frontmatter.world ?? void 0,
|
|
143
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
144
|
+
expires_at
|
|
145
|
+
};
|
|
146
|
+
return { success: true, plan, errors: [] };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/contracts/plan-contract.ts
|
|
150
|
+
var PLAN_EXIT_CODES = {
|
|
151
|
+
ON_PLAN: 0,
|
|
152
|
+
OFF_PLAN: 1,
|
|
153
|
+
CONSTRAINT_VIOLATED: 2,
|
|
154
|
+
ERROR: 3,
|
|
155
|
+
PLAN_COMPLETE: 4
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
parsePlanMarkdown,
|
|
160
|
+
PLAN_EXIT_CODES
|
|
161
|
+
};
|