@neuroverseos/governance 0.1.6 → 0.2.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.
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 +337 -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 +297 -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 +297 -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 +297 -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-Z2S2HIV5.js → chunk-2NICNKOM.js} +2 -2
  25. package/dist/{chunk-B4NF3OLW.js → chunk-4JRYGIO7.js} +56 -2
  26. package/dist/chunk-4QXB6PEO.js +232 -0
  27. package/dist/chunk-6CZSKEY5.js +164 -0
  28. package/dist/{chunk-O5OMJMIE.js → chunk-7P3S7MAY.js} +502 -2
  29. package/dist/chunk-A5W4GNQO.js +130 -0
  30. package/dist/chunk-AKW5YVCE.js +96 -0
  31. package/dist/chunk-DPVS43ZT.js +608 -0
  32. package/dist/{chunk-EIUHJXBB.js → chunk-GR6DGCZ2.js} +1 -1
  33. package/dist/chunk-KEST3MWO.js +324 -0
  34. package/dist/{chunk-D7BGWV2J.js → chunk-NF5POFCI.js} +5 -3
  35. package/dist/{chunk-FZQCRGUU.js → chunk-OHAC6HJE.js} +27 -3
  36. package/dist/chunk-OT6PXH54.js +61 -0
  37. package/dist/{chunk-ITJ3LCPG.js → chunk-PDOZHZWL.js} +1 -1
  38. package/dist/{chunk-T4X42QXC.js → chunk-Q6O7ZLO2.js} +0 -59
  39. package/dist/{chunk-FYPYZFV5.js → chunk-QPASI2BR.js} +1 -1
  40. package/dist/{chunk-EQXFOKH2.js → chunk-RWXVAH6P.js} +27 -3
  41. package/dist/{chunk-CROPZ75A.js → chunk-SKU3GAPD.js} +27 -3
  42. package/dist/chunk-YZFATT7X.js +9 -0
  43. package/dist/cli/neuroverse.cjs +5343 -732
  44. package/dist/cli/neuroverse.js +69 -13
  45. package/dist/cli/plan.cjs +1599 -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 +361 -0
  49. package/dist/cli/run.cjs +1746 -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-QV6HELS5.js +170 -0
  56. package/dist/{explain-3B3VB6TL.js → explain-IDCRWMPX.js} +2 -1
  57. package/dist/{guard-67Y66P3I.js → guard-GFLQZY6U.js} +20 -6
  58. package/dist/{guard-contract-D_RQz9kt.d.ts → guard-contract-Cm91Kp4j.d.cts} +182 -2
  59. package/dist/{guard-contract-D_RQz9kt.d.cts → guard-contract-Cm91Kp4j.d.ts} +182 -2
  60. package/dist/guard-engine-JLTUARGU.js +10 -0
  61. package/dist/{impact-CHERK3O6.js → impact-XPECYRLH.js} +5 -3
  62. package/dist/{improve-YG6I6ERG.js → improve-GPUBKTEA.js} +4 -3
  63. package/dist/index.cjs +2135 -89
  64. package/dist/index.d.cts +481 -12
  65. package/dist/index.d.ts +481 -12
  66. package/dist/index.js +70 -20
  67. package/dist/{init-Z66T6TDI.js → init-PKPIYHYE.js} +2 -0
  68. package/dist/mcp-server-LZVJHBT5.js +13 -0
  69. package/dist/model-adapter-BB7G4MFI.js +11 -0
  70. package/dist/playground-FGOMASHN.js +550 -0
  71. package/dist/redteam-SK7AMIG3.js +357 -0
  72. package/dist/session-VISISNWJ.js +14 -0
  73. package/dist/{simulate-ETHHINZ4.js → simulate-VDOYQFRO.js} +2 -1
  74. package/dist/test-75AVHC3R.js +217 -0
  75. package/dist/{trace-3YODSSIP.js → trace-JVF67VR3.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
@@ -1,3 +1,8 @@
1
+ import {
2
+ buildPlanCheck,
3
+ evaluatePlan
4
+ } from "./chunk-4QXB6PEO.js";
5
+
1
6
  // src/engine/guard-engine.ts
2
7
  var PROMPT_INJECTION_PATTERNS = [
3
8
  // Instruction override
@@ -89,6 +94,7 @@ function evaluateGuard(event, world, options = {}) {
89
94
  const eventText = (event.intent + " " + (event.tool ?? "") + " " + (event.scope ?? "")).toLowerCase();
90
95
  const invariantChecks = [];
91
96
  const safetyChecks = [];
97
+ let planCheckResult;
92
98
  const roleChecks = [];
93
99
  const guardChecks = [];
94
100
  const kernelRuleChecks = [];
@@ -116,6 +122,7 @@ function evaluateGuard(event, world, options = {}) {
116
122
  includeTrace ? buildTrace(
117
123
  invariantChecks,
118
124
  safetyChecks,
125
+ planCheckResult,
119
126
  roleChecks,
120
127
  guardChecks,
121
128
  kernelRuleChecks,
@@ -144,6 +151,7 @@ function evaluateGuard(event, world, options = {}) {
144
151
  includeTrace ? buildTrace(
145
152
  invariantChecks,
146
153
  safetyChecks,
154
+ planCheckResult,
147
155
  roleChecks,
148
156
  guardChecks,
149
157
  kernelRuleChecks,
@@ -154,6 +162,42 @@ function evaluateGuard(event, world, options = {}) {
154
162
  ) : void 0
155
163
  );
156
164
  }
165
+ if (options.plan) {
166
+ const planVerdict = evaluatePlan(event, options.plan);
167
+ planCheckResult = buildPlanCheck(event, options.plan, planVerdict);
168
+ if (!planVerdict.allowed && planVerdict.status !== "PLAN_COMPLETE") {
169
+ decidingLayer = "plan-enforcement";
170
+ decidingId = `plan-${options.plan.plan_id}`;
171
+ const planStatus = planVerdict.status === "CONSTRAINT_VIOLATED" ? "PAUSE" : "BLOCK";
172
+ let reason = planVerdict.reason ?? "Action blocked by plan.";
173
+ if (planVerdict.status === "OFF_PLAN" && planVerdict.closestStep) {
174
+ reason += ` Closest step: "${planVerdict.closestStep}" (similarity: ${(planVerdict.similarityScore ?? 0).toFixed(2)})`;
175
+ }
176
+ return buildVerdict(
177
+ planStatus,
178
+ reason,
179
+ `plan-${options.plan.plan_id}`,
180
+ void 0,
181
+ world,
182
+ level,
183
+ invariantChecks,
184
+ guardsMatched,
185
+ rulesMatched,
186
+ includeTrace ? buildTrace(
187
+ invariantChecks,
188
+ safetyChecks,
189
+ planCheckResult,
190
+ roleChecks,
191
+ guardChecks,
192
+ kernelRuleChecks,
193
+ levelChecks,
194
+ decidingLayer,
195
+ decidingId,
196
+ startTime
197
+ ) : void 0
198
+ );
199
+ }
200
+ }
157
201
  const roleVerdict = checkRoleRules(event, eventText, world, roleChecks);
158
202
  if (roleVerdict) {
159
203
  decidingLayer = "role";
@@ -171,6 +215,7 @@ function evaluateGuard(event, world, options = {}) {
171
215
  includeTrace ? buildTrace(
172
216
  invariantChecks,
173
217
  safetyChecks,
218
+ planCheckResult,
174
219
  roleChecks,
175
220
  guardChecks,
176
221
  kernelRuleChecks,
@@ -199,6 +244,7 @@ function evaluateGuard(event, world, options = {}) {
199
244
  includeTrace ? buildTrace(
200
245
  invariantChecks,
201
246
  safetyChecks,
247
+ planCheckResult,
202
248
  roleChecks,
203
249
  guardChecks,
204
250
  kernelRuleChecks,
@@ -227,6 +273,7 @@ function evaluateGuard(event, world, options = {}) {
227
273
  includeTrace ? buildTrace(
228
274
  invariantChecks,
229
275
  safetyChecks,
276
+ planCheckResult,
230
277
  roleChecks,
231
278
  guardChecks,
232
279
  kernelRuleChecks,
@@ -254,6 +301,7 @@ function evaluateGuard(event, world, options = {}) {
254
301
  includeTrace ? buildTrace(
255
302
  invariantChecks,
256
303
  safetyChecks,
304
+ planCheckResult,
257
305
  roleChecks,
258
306
  guardChecks,
259
307
  kernelRuleChecks,
@@ -278,6 +326,7 @@ function evaluateGuard(event, world, options = {}) {
278
326
  includeTrace ? buildTrace(
279
327
  invariantChecks,
280
328
  safetyChecks,
329
+ planCheckResult,
281
330
  roleChecks,
282
331
  guardChecks,
283
332
  kernelRuleChecks,
@@ -615,8 +664,8 @@ function matchesKeywords(eventText, ruleText) {
615
664
  function eventToAllowlistKey(event) {
616
665
  return `${(event.tool ?? "*").toLowerCase()}::${event.intent.toLowerCase().trim()}`;
617
666
  }
618
- function buildTrace(invariantChecks, safetyChecks, roleChecks, guardChecks, kernelRuleChecks, levelChecks, decidingLayer, decidingId, startTime) {
619
- return {
667
+ function buildTrace(invariantChecks, safetyChecks, planCheck, roleChecks, guardChecks, kernelRuleChecks, levelChecks, decidingLayer, decidingId, startTime) {
668
+ const trace = {
620
669
  invariantChecks,
621
670
  safetyChecks,
622
671
  roleChecks,
@@ -634,6 +683,7 @@ function buildTrace(invariantChecks, safetyChecks, roleChecks, guardChecks, kern
634
683
  "safety-scope-escape",
635
684
  "safety-execution-claim",
636
685
  "safety-execution-intent",
686
+ "plan-enforcement",
637
687
  "role-rules",
638
688
  "declarative-guards",
639
689
  "kernel-rules",
@@ -643,6 +693,10 @@ function buildTrace(invariantChecks, safetyChecks, roleChecks, guardChecks, kern
643
693
  },
644
694
  durationMs: performance.now() - startTime
645
695
  };
696
+ if (planCheck) {
697
+ trace.planCheck = planCheck;
698
+ }
699
+ return trace;
646
700
  }
647
701
  function buildVerdict(status, reason, ruleId, warning, world, level, invariantChecks, guardsMatched, rulesMatched, trace) {
648
702
  const evidence = {
@@ -0,0 +1,232 @@
1
+ // src/engine/plan-engine.ts
2
+ function keywordMatch(eventText, step) {
3
+ const stepText = [
4
+ step.label,
5
+ step.description ?? "",
6
+ ...step.tags ?? []
7
+ ].join(" ").toLowerCase();
8
+ const keywords = stepText.split(/\s+/).filter((w) => w.length > 3);
9
+ if (keywords.length === 0) return false;
10
+ const matched = keywords.filter((kw) => eventText.includes(kw));
11
+ return matched.length >= Math.ceil(keywords.length * 0.5);
12
+ }
13
+ function tokenSimilarity(a, b) {
14
+ const tokensA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
15
+ const tokensB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
16
+ if (tokensA.size === 0 || tokensB.size === 0) return 0;
17
+ let intersection = 0;
18
+ for (const t of tokensA) {
19
+ if (tokensB.has(t)) intersection++;
20
+ }
21
+ const union = (/* @__PURE__ */ new Set([...tokensA, ...tokensB])).size;
22
+ return union > 0 ? intersection / union : 0;
23
+ }
24
+ function findMatchingStep(eventText, event, steps) {
25
+ const pendingOrActive = steps.filter((s) => s.status === "pending" || s.status === "active");
26
+ if (pendingOrActive.length === 0) {
27
+ return { matched: null, closest: null, closestScore: 0 };
28
+ }
29
+ for (const step of pendingOrActive) {
30
+ if (keywordMatch(eventText, step)) {
31
+ if (step.tools && event.tool && !step.tools.includes(event.tool)) {
32
+ continue;
33
+ }
34
+ return { matched: step, closest: step, closestScore: 1 };
35
+ }
36
+ }
37
+ const intentText = [event.intent, event.tool ?? "", event.scope ?? ""].join(" ");
38
+ let bestStep = null;
39
+ let bestScore = 0;
40
+ for (const step of pendingOrActive) {
41
+ const stepText = [step.label, step.description ?? "", ...step.tags ?? []].join(" ");
42
+ const score = tokenSimilarity(intentText, stepText);
43
+ if (score > bestScore) {
44
+ bestScore = score;
45
+ bestStep = step;
46
+ }
47
+ }
48
+ const SIMILARITY_THRESHOLD = 0.35;
49
+ if (bestScore >= SIMILARITY_THRESHOLD && bestStep) {
50
+ if (bestStep.tools && event.tool && !bestStep.tools.includes(event.tool)) {
51
+ return { matched: null, closest: bestStep, closestScore: bestScore };
52
+ }
53
+ return { matched: bestStep, closest: bestStep, closestScore: bestScore };
54
+ }
55
+ return { matched: null, closest: bestStep, closestScore: bestScore };
56
+ }
57
+ function isSequenceValid(step, plan) {
58
+ if (!plan.sequential) return true;
59
+ if (!step.requires || step.requires.length === 0) return true;
60
+ return step.requires.every((reqId) => {
61
+ const reqStep = plan.steps.find((s) => s.id === reqId);
62
+ return reqStep?.status === "completed";
63
+ });
64
+ }
65
+ function checkConstraints(event, eventText, constraints) {
66
+ const checks = [];
67
+ for (const constraint of constraints) {
68
+ if (constraint.type === "approval") {
69
+ if (constraint.trigger && eventText.includes(constraint.trigger.substring(0, 10).toLowerCase())) {
70
+ checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
71
+ return { violated: constraint, checks };
72
+ }
73
+ const keywords = constraint.description.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
74
+ const relevant = keywords.some((kw) => eventText.includes(kw));
75
+ if (relevant) {
76
+ checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
77
+ return { violated: constraint, checks };
78
+ }
79
+ checks.push({ constraintId: constraint.id, passed: true });
80
+ continue;
81
+ }
82
+ if (constraint.type === "scope" && constraint.trigger) {
83
+ const keywords = constraint.trigger.split(/\s+/).filter((w) => w.length > 3);
84
+ const violated = keywords.length > 0 && keywords.every((kw) => eventText.includes(kw));
85
+ checks.push({
86
+ constraintId: constraint.id,
87
+ passed: !violated,
88
+ reason: violated ? constraint.description : void 0
89
+ });
90
+ if (violated) {
91
+ return { violated: constraint, checks };
92
+ }
93
+ continue;
94
+ }
95
+ checks.push({ constraintId: constraint.id, passed: true });
96
+ }
97
+ return { violated: null, checks };
98
+ }
99
+ function getPlanProgress(plan) {
100
+ const completed = plan.steps.filter((s) => s.status === "completed").length;
101
+ const total = plan.steps.length;
102
+ return {
103
+ completed,
104
+ total,
105
+ percentage: total > 0 ? Math.round(completed / total * 100) : 0
106
+ };
107
+ }
108
+ function advancePlan(plan, stepId, evidence) {
109
+ const step = plan.steps.find((s) => s.id === stepId);
110
+ if (!step) {
111
+ return { success: false, reason: `Step "${stepId}" not found in plan.` };
112
+ }
113
+ if (step.status === "completed") {
114
+ return { success: false, reason: `Step "${stepId}" is already completed.` };
115
+ }
116
+ const mode = plan.completion ?? "trust";
117
+ if (mode === "verified" && step.verify) {
118
+ if (!evidence) {
119
+ return {
120
+ success: false,
121
+ reason: `Step "${step.label}" requires evidence (verify: ${step.verify}). Provide evidence to advance.`
122
+ };
123
+ }
124
+ if (evidence.type !== step.verify) {
125
+ return {
126
+ success: false,
127
+ reason: `Evidence type "${evidence.type}" does not match required verification "${step.verify}".`
128
+ };
129
+ }
130
+ }
131
+ const updatedPlan = {
132
+ ...plan,
133
+ steps: plan.steps.map(
134
+ (s) => s.id === stepId ? { ...s, status: "completed" } : s
135
+ )
136
+ };
137
+ return {
138
+ success: true,
139
+ plan: updatedPlan,
140
+ evidence: evidence ?? void 0
141
+ };
142
+ }
143
+ function evaluatePlan(event, plan) {
144
+ const progress = getPlanProgress(plan);
145
+ if (plan.expires_at) {
146
+ const expiresAt = new Date(plan.expires_at).getTime();
147
+ if (Date.now() > expiresAt) {
148
+ return {
149
+ allowed: true,
150
+ status: "PLAN_COMPLETE",
151
+ reason: "Plan has expired.",
152
+ progress
153
+ };
154
+ }
155
+ }
156
+ if (progress.completed === progress.total) {
157
+ return {
158
+ allowed: true,
159
+ status: "PLAN_COMPLETE",
160
+ reason: "All plan steps are completed.",
161
+ progress
162
+ };
163
+ }
164
+ const eventText = [
165
+ event.intent,
166
+ event.tool ?? "",
167
+ event.scope ?? ""
168
+ ].join(" ").toLowerCase();
169
+ const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
170
+ if (!matched) {
171
+ return {
172
+ allowed: false,
173
+ status: "OFF_PLAN",
174
+ reason: "Action does not match any plan step.",
175
+ closestStep: closest?.label,
176
+ similarityScore: closestScore,
177
+ progress
178
+ };
179
+ }
180
+ if (!isSequenceValid(matched, plan)) {
181
+ const pendingDeps = (matched.requires ?? []).filter((reqId) => plan.steps.find((s) => s.id === reqId)?.status !== "completed").join(", ");
182
+ return {
183
+ allowed: false,
184
+ status: "OFF_PLAN",
185
+ reason: `Step "${matched.label}" requires completion of: ${pendingDeps}`,
186
+ matchedStep: matched.id,
187
+ progress
188
+ };
189
+ }
190
+ const { violated } = checkConstraints(event, eventText, plan.constraints);
191
+ if (violated) {
192
+ return {
193
+ allowed: false,
194
+ status: "CONSTRAINT_VIOLATED",
195
+ reason: violated.description,
196
+ matchedStep: matched.id,
197
+ progress
198
+ };
199
+ }
200
+ return {
201
+ allowed: true,
202
+ status: "ON_PLAN",
203
+ reason: `Matches step: ${matched.label}`,
204
+ matchedStep: matched.id,
205
+ progress
206
+ };
207
+ }
208
+ function buildPlanCheck(event, plan, verdict) {
209
+ const eventText = [event.intent, event.tool ?? "", event.scope ?? ""].join(" ").toLowerCase();
210
+ const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
211
+ const { checks: constraintChecks } = checkConstraints(event, eventText, plan.constraints);
212
+ const progress = getPlanProgress(plan);
213
+ return {
214
+ planId: plan.plan_id,
215
+ matched: !!matched,
216
+ matchedStepId: matched?.id,
217
+ matchedStepLabel: matched?.label,
218
+ closestStepId: !matched ? closest?.id : void 0,
219
+ closestStepLabel: !matched ? closest?.label : void 0,
220
+ similarityScore: !matched ? closestScore : void 0,
221
+ sequenceValid: matched ? isSequenceValid(matched, plan) : void 0,
222
+ constraintsChecked: constraintChecks,
223
+ progress: { completed: progress.completed, total: progress.total }
224
+ };
225
+ }
226
+
227
+ export {
228
+ getPlanProgress,
229
+ advancePlan,
230
+ evaluatePlan,
231
+ buildPlanCheck
232
+ };
@@ -0,0 +1,164 @@
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 completionRaw = frontmatter.completion?.toLowerCase();
137
+ const completion = completionRaw === "verified" ? "verified" : "trust";
138
+ const plan = {
139
+ plan_id: frontmatter.plan_id,
140
+ objective: frontmatter.objective ?? "",
141
+ sequential: frontmatter.sequential === "true",
142
+ completion,
143
+ steps,
144
+ constraints,
145
+ world_id: frontmatter.world ?? void 0,
146
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
147
+ expires_at
148
+ };
149
+ return { success: true, plan, errors: [] };
150
+ }
151
+
152
+ // src/contracts/plan-contract.ts
153
+ var PLAN_EXIT_CODES = {
154
+ ON_PLAN: 0,
155
+ OFF_PLAN: 1,
156
+ CONSTRAINT_VIOLATED: 2,
157
+ ERROR: 3,
158
+ PLAN_COMPLETE: 4
159
+ };
160
+
161
+ export {
162
+ parsePlanMarkdown,
163
+ PLAN_EXIT_CODES
164
+ };