@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.
Files changed (82) hide show
  1. package/README.md +279 -423
  2. package/dist/adapters/express.cjs +242 -2
  3. package/dist/adapters/express.d.cts +1 -1
  4. package/dist/adapters/express.d.ts +1 -1
  5. package/dist/adapters/express.js +5 -3
  6. package/dist/adapters/index.cjs +301 -5
  7. package/dist/adapters/index.d.cts +1 -1
  8. package/dist/adapters/index.d.ts +1 -1
  9. package/dist/adapters/index.js +8 -6
  10. package/dist/adapters/langchain.cjs +267 -3
  11. package/dist/adapters/langchain.d.cts +8 -1
  12. package/dist/adapters/langchain.d.ts +8 -1
  13. package/dist/adapters/langchain.js +5 -3
  14. package/dist/adapters/openai.cjs +267 -3
  15. package/dist/adapters/openai.d.cts +8 -1
  16. package/dist/adapters/openai.d.ts +8 -1
  17. package/dist/adapters/openai.js +5 -3
  18. package/dist/adapters/openclaw.cjs +267 -3
  19. package/dist/adapters/openclaw.d.cts +8 -1
  20. package/dist/adapters/openclaw.d.ts +8 -1
  21. package/dist/adapters/openclaw.js +5 -3
  22. package/dist/{bootstrap-H4HHKQ5G.js → bootstrap-GXVDZNF7.js} +2 -1
  23. package/dist/{build-73KAVHEY.js → build-P42YFKQV.js} +34 -3
  24. package/dist/{chunk-FYPYZFV5.js → chunk-2JQJ5U5X.js} +1 -1
  25. package/dist/chunk-37JG24WH.js +161 -0
  26. package/dist/chunk-5EDDNJU6.js +321 -0
  27. package/dist/{chunk-O5OMJMIE.js → chunk-7P3S7MAY.js} +502 -2
  28. package/dist/chunk-A5W4GNQO.js +130 -0
  29. package/dist/{chunk-ITJ3LCPG.js → chunk-ADV7Q2LJ.js} +1 -1
  30. package/dist/chunk-AKW5YVCE.js +96 -0
  31. package/dist/{chunk-EIUHJXBB.js → chunk-GR6DGCZ2.js} +1 -1
  32. package/dist/{chunk-EQXFOKH2.js → chunk-IVPKFJX3.js} +24 -3
  33. package/dist/{chunk-D7BGWV2J.js → chunk-NF5POFCI.js} +5 -3
  34. package/dist/chunk-OT6PXH54.js +61 -0
  35. package/dist/chunk-P74Y66ZV.js +205 -0
  36. package/dist/chunk-PAX2P6ZP.js +601 -0
  37. package/dist/{chunk-B4NF3OLW.js → chunk-PQBJBVSW.js} +56 -2
  38. package/dist/{chunk-T4X42QXC.js → chunk-Q6O7ZLO2.js} +0 -59
  39. package/dist/{chunk-FZQCRGUU.js → chunk-TINSRYXQ.js} +24 -3
  40. package/dist/{chunk-CROPZ75A.js → chunk-UPJNTSVM.js} +24 -3
  41. package/dist/chunk-YZFATT7X.js +9 -0
  42. package/dist/{chunk-Z2S2HIV5.js → chunk-ZL4AHY4X.js} +2 -2
  43. package/dist/cli/neuroverse.cjs +5287 -740
  44. package/dist/cli/neuroverse.js +69 -13
  45. package/dist/cli/plan.cjs +1554 -0
  46. package/dist/cli/plan.d.cts +20 -0
  47. package/dist/cli/plan.d.ts +20 -0
  48. package/dist/cli/plan.js +346 -0
  49. package/dist/cli/run.cjs +1716 -0
  50. package/dist/cli/run.d.cts +20 -0
  51. package/dist/cli/run.d.ts +20 -0
  52. package/dist/cli/run.js +143 -0
  53. package/dist/{configure-ai-46JVG56I.js → configure-ai-TK67ZWZL.js} +5 -2
  54. package/dist/{derive-6NAEWLM5.js → derive-TLIV4OOU.js} +6 -4
  55. package/dist/doctor-V72UM2TC.js +170 -0
  56. package/dist/{explain-3B3VB6TL.js → explain-IDCRWMPX.js} +2 -1
  57. package/dist/{guard-67Y66P3I.js → guard-WA3FCCIO.js} +20 -6
  58. package/dist/{guard-contract-D_RQz9kt.d.ts → guard-contract-D-2LQInm.d.cts} +144 -2
  59. package/dist/{guard-contract-D_RQz9kt.d.cts → guard-contract-D-2LQInm.d.ts} +144 -2
  60. package/dist/guard-engine-D7X4CVAE.js +10 -0
  61. package/dist/{impact-CHERK3O6.js → impact-BWULZ5RP.js} +5 -3
  62. package/dist/{improve-YG6I6ERG.js → improve-GPUBKTEA.js} +4 -3
  63. package/dist/index.cjs +2095 -89
  64. package/dist/index.d.cts +466 -12
  65. package/dist/index.d.ts +466 -12
  66. package/dist/index.js +70 -20
  67. package/dist/{init-Z66T6TDI.js → init-PKPIYHYE.js} +2 -0
  68. package/dist/mcp-server-YUOQP4M5.js +13 -0
  69. package/dist/model-adapter-BB7G4MFI.js +11 -0
  70. package/dist/playground-CBXMAW2B.js +550 -0
  71. package/dist/redteam-SSNABQ7W.js +357 -0
  72. package/dist/session-MWRBTCYX.js +14 -0
  73. package/dist/{simulate-ETHHINZ4.js → simulate-VDOYQFRO.js} +2 -1
  74. package/dist/test-3GZSG5FR.js +217 -0
  75. package/dist/{trace-3YODSSIP.js → trace-TM4Z7G73.js} +4 -2
  76. package/dist/{validate-UVE6GKQU.js → validate-LLBWVPGV.js} +15 -6
  77. package/dist/validate-engine-UIABSIHD.js +7 -0
  78. package/dist/{world-WLNHL5XC.js → world-LAXO6DOX.js} +87 -7
  79. package/dist/world-loader-HMPTOEA2.js +9 -0
  80. package/package.json +19 -5
  81. package/dist/validate-engine-657D75OG.js +0 -6
  82. /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
- return {
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-D_RQz9kt.cjs';
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-D_RQz9kt.js';
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-CROPZ75A.js";
7
- import "../chunk-B4NF3OLW.js";
8
- import "../chunk-M3TZFGHO.js";
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-657D75OG.js");
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-D7BGWV2J.js";
5
+ } from "./chunk-NF5POFCI.js";
6
6
  import {
7
7
  DERIVE_EXIT_CODES
8
- } from "./chunk-T4X42QXC.js";
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-O5OMJMIE.js";
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
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  evaluateGuard
3
- } from "./chunk-B4NF3OLW.js";
3
+ } from "./chunk-PQBJBVSW.js";
4
4
 
5
5
  // src/engine/audit-logger.ts
6
6
  var FileAuditLogger = class {
@@ -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
+ };