@neuroverseos/governance 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.well-known/ai-plugin.json +34 -9
- package/AGENTS.md +72 -24
- package/README.md +343 -248
- package/dist/adapters/autoresearch.cjs +1345 -0
- package/dist/adapters/autoresearch.d.cts +111 -0
- package/dist/adapters/autoresearch.d.ts +111 -0
- package/dist/adapters/autoresearch.js +12 -0
- package/dist/adapters/deep-agents.cjs +1528 -0
- package/dist/adapters/deep-agents.d.cts +181 -0
- package/dist/adapters/deep-agents.d.ts +181 -0
- package/dist/adapters/deep-agents.js +17 -0
- package/dist/adapters/express.cjs +1253 -0
- package/dist/adapters/express.d.cts +66 -0
- package/dist/adapters/express.d.ts +66 -0
- package/dist/adapters/express.js +12 -0
- package/dist/adapters/index.cjs +2112 -0
- package/dist/adapters/index.d.cts +8 -0
- package/dist/adapters/index.d.ts +8 -0
- package/dist/adapters/index.js +68 -0
- package/dist/adapters/langchain.cjs +1315 -0
- package/dist/adapters/langchain.d.cts +89 -0
- package/dist/adapters/langchain.d.ts +89 -0
- package/dist/adapters/langchain.js +17 -0
- package/dist/adapters/openai.cjs +1345 -0
- package/dist/adapters/openai.d.cts +99 -0
- package/dist/adapters/openai.d.ts +99 -0
- package/dist/adapters/openai.js +17 -0
- package/dist/adapters/openclaw.cjs +1337 -0
- package/dist/adapters/openclaw.d.cts +99 -0
- package/dist/adapters/openclaw.d.ts +99 -0
- package/dist/adapters/openclaw.js +17 -0
- package/dist/add-ROOZLU62.js +314 -0
- package/dist/behavioral-MJO34S6Q.js +118 -0
- package/dist/bootstrap-CQRZVOXK.js +116 -0
- package/dist/bootstrap-emitter-Q7UIJZ2O.js +7 -0
- package/dist/bootstrap-parser-EEF36XDU.js +7 -0
- package/dist/browser.global.js +941 -0
- package/dist/build-QKOBBC23.js +341 -0
- package/dist/chunk-3WQLXYTP.js +91 -0
- package/dist/chunk-4FLICVVA.js +119 -0
- package/dist/chunk-4NGDRRQH.js +10 -0
- package/dist/chunk-5TPFNWRU.js +215 -0
- package/dist/chunk-5U2MQO5P.js +57 -0
- package/dist/chunk-6CZSKEY5.js +164 -0
- package/dist/chunk-6S5CFQXY.js +624 -0
- package/dist/chunk-7P3S7MAY.js +1090 -0
- package/dist/chunk-A5W4GNQO.js +130 -0
- package/dist/chunk-A7GKPPU7.js +226 -0
- package/dist/chunk-AKW5YVCE.js +96 -0
- package/dist/chunk-B6OXJLJ5.js +622 -0
- package/dist/chunk-BNKJPUPQ.js +113 -0
- package/dist/chunk-BQZMOEML.js +43 -0
- package/dist/chunk-CNSO6XW5.js +207 -0
- package/dist/chunk-CTZHONLA.js +135 -0
- package/dist/chunk-D2UCV5AK.js +326 -0
- package/dist/chunk-EMQDLDAF.js +458 -0
- package/dist/chunk-F66BVUYB.js +340 -0
- package/dist/chunk-G7DJ6VOD.js +101 -0
- package/dist/chunk-I3RRAYK2.js +11 -0
- package/dist/chunk-IS4WUH6Y.js +363 -0
- package/dist/chunk-MH7BT4VH.js +15 -0
- package/dist/chunk-O5ABKEA7.js +304 -0
- package/dist/chunk-OT6PXH54.js +61 -0
- package/dist/chunk-PVTQQS3Y.js +186 -0
- package/dist/chunk-Q6O7ZLO2.js +62 -0
- package/dist/chunk-QLPTHTVB.js +253 -0
- package/dist/chunk-QWGCMQQD.js +16 -0
- package/dist/chunk-QXBFT7NI.js +201 -0
- package/dist/chunk-TG6SEF24.js +246 -0
- package/dist/chunk-U6U7EJZL.js +177 -0
- package/dist/chunk-W7LLXRGY.js +830 -0
- package/dist/chunk-ZJTDUCC2.js +194 -0
- package/dist/chunk-ZWI3NIXK.js +314 -0
- package/dist/cli/neuroverse.cjs +14191 -0
- package/dist/cli/neuroverse.d.cts +1 -0
- package/dist/cli/neuroverse.d.ts +1 -0
- package/dist/cli/neuroverse.js +227 -0
- package/dist/cli/plan.cjs +2439 -0
- package/dist/cli/plan.d.cts +20 -0
- package/dist/cli/plan.d.ts +20 -0
- package/dist/cli/plan.js +353 -0
- package/dist/cli/run.cjs +2001 -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-6TZ3MCSI.js +132 -0
- package/dist/decision-flow-M63D47LO.js +61 -0
- package/dist/demo-G43RLCPK.js +469 -0
- package/dist/derive-FJZVIPUZ.js +153 -0
- package/dist/doctor-6BC6X2VO.js +173 -0
- package/dist/equity-penalties-SG5IZQ7I.js +244 -0
- package/dist/explain-RHBU2GBR.js +51 -0
- package/dist/guard-AJCCGZMF.js +92 -0
- package/dist/guard-contract-DqFcTScd.d.cts +821 -0
- package/dist/guard-contract-DqFcTScd.d.ts +821 -0
- package/dist/guard-engine-PNR6MHCM.js +10 -0
- package/dist/impact-3XVDSCBU.js +59 -0
- package/dist/improve-TQP4ECSY.js +66 -0
- package/dist/index.cjs +7591 -0
- package/dist/index.d.cts +2195 -0
- package/dist/index.d.ts +2195 -0
- package/dist/index.js +472 -0
- package/dist/infer-world-IFXCACJ5.js +543 -0
- package/dist/init-FYPV4SST.js +144 -0
- package/dist/init-world-TI7ARHBT.js +223 -0
- package/dist/mcp-server-5Y3ZM7TV.js +13 -0
- package/dist/model-adapter-VXEKB4LS.js +11 -0
- package/dist/playground-VZBNPPBO.js +560 -0
- package/dist/redteam-MZPZD3EF.js +357 -0
- package/dist/session-JYOARW54.js +15 -0
- package/dist/shared-7RLUHNMU.js +16 -0
- package/dist/shared-B8dvUUD8.d.cts +60 -0
- package/dist/shared-Dr5Wiay8.d.ts +60 -0
- package/dist/simulate-LJXYBC6M.js +83 -0
- package/dist/test-BOOR4A5F.js +217 -0
- package/dist/trace-PKV4KX56.js +166 -0
- package/dist/validate-RALX7CZS.js +81 -0
- package/dist/validate-engine-7ZXFVGF2.js +7 -0
- package/dist/viz/assets/index-B8SaeJZZ.js +23 -0
- package/dist/viz/index.html +23 -0
- package/dist/world-BIP4GZBZ.js +376 -0
- package/dist/world-loader-Y6HMQH2D.js +13 -0
- package/dist/worlds/autoresearch.nv-world.md +230 -0
- package/dist/worlds/coding-agent.nv-world.md +211 -0
- package/dist/worlds/derivation-world.nv-world.md +278 -0
- package/dist/worlds/research-agent.nv-world.md +169 -0
- package/dist/worlds/social-media.nv-world.md +198 -0
- package/dist/worlds/trading-agent.nv-world.md +218 -0
- package/examples/social-media-sim/bridge.py +209 -0
- package/examples/social-media-sim/simulation.py +927 -0
- package/package.json +16 -3
- package/simulate.html +4 -336
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import {
|
|
2
|
+
validateWorld
|
|
3
|
+
} from "./chunk-7P3S7MAY.js";
|
|
4
|
+
|
|
5
|
+
// src/engine/add-engine.ts
|
|
6
|
+
function slugify(text) {
|
|
7
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "").slice(0, 60);
|
|
8
|
+
}
|
|
9
|
+
async function addGuard(worldDir, input) {
|
|
10
|
+
const { readFile, writeFile } = await import("fs/promises");
|
|
11
|
+
const { join } = await import("path");
|
|
12
|
+
const guardsPath = join(worldDir, "guards.json");
|
|
13
|
+
let config;
|
|
14
|
+
try {
|
|
15
|
+
const raw = await readFile(guardsPath, "utf-8");
|
|
16
|
+
config = JSON.parse(raw);
|
|
17
|
+
} catch {
|
|
18
|
+
config = { guards: [], intent_vocabulary: {} };
|
|
19
|
+
}
|
|
20
|
+
const id = input.id ?? `guard_${slugify(input.label)}`;
|
|
21
|
+
if (config.guards.some((g) => g.id === id)) {
|
|
22
|
+
throw new Error(`Guard with id "${id}" already exists. Use a different label or provide --id.`);
|
|
23
|
+
}
|
|
24
|
+
const guard = {
|
|
25
|
+
id,
|
|
26
|
+
label: input.label,
|
|
27
|
+
description: input.description ?? input.label,
|
|
28
|
+
category: input.category ?? "operational",
|
|
29
|
+
enforcement: input.enforcement,
|
|
30
|
+
immutable: false,
|
|
31
|
+
intent_patterns: input.intentPatterns,
|
|
32
|
+
...input.appliesTo && { appliesTo: input.appliesTo },
|
|
33
|
+
...input.invariantRef && { invariant_ref: input.invariantRef },
|
|
34
|
+
...input.enforcement === "modify" && input.reason && { modify_to: input.reason }
|
|
35
|
+
};
|
|
36
|
+
config.guards.push(guard);
|
|
37
|
+
await writeFile(guardsPath, JSON.stringify(config, null, 2) + "\n");
|
|
38
|
+
const { loadWorldFromDirectory } = await import("./world-loader-Y6HMQH2D.js");
|
|
39
|
+
const world = await loadWorldFromDirectory(worldDir);
|
|
40
|
+
const report = validateWorld(world);
|
|
41
|
+
return {
|
|
42
|
+
type: "guard",
|
|
43
|
+
id,
|
|
44
|
+
file: guardsPath,
|
|
45
|
+
valid: report.summary.isHealthy,
|
|
46
|
+
findings: report.findings,
|
|
47
|
+
construct: guard
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function addRule(worldDir, input) {
|
|
51
|
+
const { readFile, writeFile, mkdir } = await import("fs/promises");
|
|
52
|
+
const { join } = await import("path");
|
|
53
|
+
const { readdirSync } = await import("fs");
|
|
54
|
+
const rulesDir = join(worldDir, "rules");
|
|
55
|
+
await mkdir(rulesDir, { recursive: true });
|
|
56
|
+
let nextNum = 1;
|
|
57
|
+
try {
|
|
58
|
+
const existing = readdirSync(rulesDir).filter((f) => f.match(/^rule-\d+\.json$/)).sort();
|
|
59
|
+
if (existing.length > 0) {
|
|
60
|
+
const lastFile = existing[existing.length - 1];
|
|
61
|
+
const match = lastFile.match(/rule-(\d+)\.json/);
|
|
62
|
+
if (match) nextNum = parseInt(match[1], 10) + 1;
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
const ruleNum = String(nextNum).padStart(3, "0");
|
|
67
|
+
const id = input.id ?? `rule_${slugify(input.label)}`;
|
|
68
|
+
const rule = {
|
|
69
|
+
id,
|
|
70
|
+
severity: input.severity,
|
|
71
|
+
label: input.label,
|
|
72
|
+
description: input.description ?? input.label,
|
|
73
|
+
order: nextNum,
|
|
74
|
+
triggers: input.triggers,
|
|
75
|
+
...input.effects && { effects: input.effects },
|
|
76
|
+
...input.collapseCheck && { collapse_check: input.collapseCheck },
|
|
77
|
+
causal_translation: input.causalTranslation ?? {
|
|
78
|
+
trigger_text: `Triggers when: ${input.triggers.map((t) => `${t.field} ${t.operator} ${t.value}`).join(" AND ")}`,
|
|
79
|
+
rule_text: input.label,
|
|
80
|
+
shift_text: input.description ?? input.label,
|
|
81
|
+
effect_text: input.effects ? input.effects.map((e) => `${e.target} ${e.operation} ${e.value}`).join(", ") : "No direct effects"
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const rulePath = join(rulesDir, `rule-${ruleNum}.json`);
|
|
85
|
+
await writeFile(rulePath, JSON.stringify(rule, null, 2) + "\n");
|
|
86
|
+
const { loadWorldFromDirectory } = await import("./world-loader-Y6HMQH2D.js");
|
|
87
|
+
const world = await loadWorldFromDirectory(worldDir);
|
|
88
|
+
const report = validateWorld(world);
|
|
89
|
+
return {
|
|
90
|
+
type: "rule",
|
|
91
|
+
id,
|
|
92
|
+
file: rulePath,
|
|
93
|
+
valid: report.summary.isHealthy,
|
|
94
|
+
findings: report.findings,
|
|
95
|
+
construct: rule
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async function addInvariant(worldDir, input) {
|
|
99
|
+
const { readFile, writeFile } = await import("fs/promises");
|
|
100
|
+
const { join } = await import("path");
|
|
101
|
+
const invariantsPath = join(worldDir, "invariants.json");
|
|
102
|
+
let config;
|
|
103
|
+
try {
|
|
104
|
+
const raw = await readFile(invariantsPath, "utf-8");
|
|
105
|
+
config = JSON.parse(raw);
|
|
106
|
+
} catch {
|
|
107
|
+
config = { invariants: [] };
|
|
108
|
+
}
|
|
109
|
+
const id = input.id ?? slugify(input.label);
|
|
110
|
+
if (config.invariants.some((inv) => inv.id === id)) {
|
|
111
|
+
throw new Error(`Invariant with id "${id}" already exists.`);
|
|
112
|
+
}
|
|
113
|
+
const invariant = {
|
|
114
|
+
id,
|
|
115
|
+
label: input.label,
|
|
116
|
+
enforcement: input.enforcement ?? "structural",
|
|
117
|
+
mutable: false
|
|
118
|
+
};
|
|
119
|
+
config.invariants.push(invariant);
|
|
120
|
+
await writeFile(invariantsPath, JSON.stringify(config, null, 2) + "\n");
|
|
121
|
+
const { loadWorldFromDirectory } = await import("./world-loader-Y6HMQH2D.js");
|
|
122
|
+
const world = await loadWorldFromDirectory(worldDir);
|
|
123
|
+
const report = validateWorld(world);
|
|
124
|
+
return {
|
|
125
|
+
type: "invariant",
|
|
126
|
+
id,
|
|
127
|
+
file: invariantsPath,
|
|
128
|
+
valid: report.summary.isHealthy,
|
|
129
|
+
findings: report.findings,
|
|
130
|
+
construct: invariant
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function classifyIntent(text) {
|
|
134
|
+
const lower = text.toLowerCase().trim();
|
|
135
|
+
const guardPatterns = [
|
|
136
|
+
/^block\b/,
|
|
137
|
+
/^prevent\b/,
|
|
138
|
+
/^deny\b/,
|
|
139
|
+
/^reject\b/,
|
|
140
|
+
/^disallow\b/,
|
|
141
|
+
/^forbid\b/,
|
|
142
|
+
/^stop\b/,
|
|
143
|
+
/^pause\b/,
|
|
144
|
+
/^warn\s+(when|if|on|before)\b/,
|
|
145
|
+
/^require\s+approval\b/,
|
|
146
|
+
/^flag\b/,
|
|
147
|
+
/^restrict\b/
|
|
148
|
+
];
|
|
149
|
+
const invariantPatterns = [
|
|
150
|
+
/must\s+(always|never)\b/,
|
|
151
|
+
/must\s+not\b/,
|
|
152
|
+
/shall\s+(always|never)\b/,
|
|
153
|
+
/can\s*not\s+(ever|drop|exceed|go)\b/,
|
|
154
|
+
/cannot\s+(ever|drop|exceed|go)\b/,
|
|
155
|
+
/\bmust\s+remain\b/,
|
|
156
|
+
/\balways\s+be\b/,
|
|
157
|
+
/\bnever\s+(exceed|drop|go|fall|be)\b/,
|
|
158
|
+
/\bfloor\b.*\bmust\b/,
|
|
159
|
+
/\bcap\b.*\bmust\b/,
|
|
160
|
+
/\bhard\s+limit\b/,
|
|
161
|
+
/\bguarantee\b/
|
|
162
|
+
];
|
|
163
|
+
const rulePatterns = [
|
|
164
|
+
/^if\b.*\bthen\b/,
|
|
165
|
+
/^when\b.*\b(reduce|increase|multiply|set|add|subtract)\b/,
|
|
166
|
+
/\b(reduce|increase|multiply|degrade|boost)\b.*\bby\b/,
|
|
167
|
+
/\b(viability|margin|score|trust|budget)\b.*\b(drop|rise|change|shift)/,
|
|
168
|
+
/\beffect\b/,
|
|
169
|
+
/\btrigger\b.*\bwhen\b/,
|
|
170
|
+
/\bcollapse\b.*\bif\b/
|
|
171
|
+
];
|
|
172
|
+
const guardScore = guardPatterns.filter((p) => p.test(lower)).length;
|
|
173
|
+
const invariantScore = invariantPatterns.filter((p) => p.test(lower)).length;
|
|
174
|
+
const ruleScore = rulePatterns.filter((p) => p.test(lower)).length;
|
|
175
|
+
const max = Math.max(guardScore, invariantScore, ruleScore);
|
|
176
|
+
if (max === 0) return "ambiguous";
|
|
177
|
+
const scores = [guardScore, invariantScore, ruleScore];
|
|
178
|
+
const winners = scores.filter((s) => s === max);
|
|
179
|
+
if (winners.length > 1) return "ambiguous";
|
|
180
|
+
if (guardScore === max) return "guard";
|
|
181
|
+
if (invariantScore === max) return "invariant";
|
|
182
|
+
return "rule";
|
|
183
|
+
}
|
|
184
|
+
function parseGuardDescription(text) {
|
|
185
|
+
const lower = text.toLowerCase().trim();
|
|
186
|
+
let enforcement = "block";
|
|
187
|
+
if (/^pause\b|require\s+approval/i.test(lower)) enforcement = "pause";
|
|
188
|
+
else if (/^warn\b|^flag\b/i.test(lower)) enforcement = "warn";
|
|
189
|
+
else if (/^block\b|^prevent\b|^deny\b|^reject\b|^forbid\b|^disallow\b|^stop\b/i.test(lower)) enforcement = "block";
|
|
190
|
+
const stripped = text.replace(/^(block|prevent|deny|reject|forbid|disallow|stop|pause|warn|flag|restrict)\s+(if\s+|when\s+|on\s+|before\s+)?/i, "").trim();
|
|
191
|
+
const words = stripped.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
192
|
+
const patterns = [];
|
|
193
|
+
if (words.length > 0) {
|
|
194
|
+
patterns.push(`*${words.join("*")}*`);
|
|
195
|
+
if (words.length > 1) {
|
|
196
|
+
for (const word of words) {
|
|
197
|
+
patterns.push(`*${word}*`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
label: text,
|
|
203
|
+
enforcement,
|
|
204
|
+
intentPatterns: patterns.length > 0 ? patterns : ["*"],
|
|
205
|
+
description: text
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export {
|
|
210
|
+
addGuard,
|
|
211
|
+
addRule,
|
|
212
|
+
addInvariant,
|
|
213
|
+
classifyIntent,
|
|
214
|
+
parseGuardDescription
|
|
215
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
advancePlan,
|
|
3
|
+
evaluatePlan,
|
|
4
|
+
getPlanProgress
|
|
5
|
+
} from "./chunk-QLPTHTVB.js";
|
|
6
|
+
|
|
7
|
+
// src/adapters/shared.ts
|
|
8
|
+
var GovernanceBlockedError = class extends Error {
|
|
9
|
+
verdict;
|
|
10
|
+
constructor(verdict, message) {
|
|
11
|
+
super(message ?? `[NeuroVerse] BLOCKED: ${verdict.reason ?? verdict.ruleId ?? "governance rule"}`);
|
|
12
|
+
this.name = "GovernanceBlockedError";
|
|
13
|
+
this.verdict = verdict;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function trackPlanProgress(event, state, callbacks) {
|
|
17
|
+
if (!state.activePlan) return;
|
|
18
|
+
const planVerdict = evaluatePlan(event, state.activePlan);
|
|
19
|
+
if (planVerdict.matchedStep) {
|
|
20
|
+
const advResult = advancePlan(state.activePlan, planVerdict.matchedStep);
|
|
21
|
+
if (advResult.success && advResult.plan) {
|
|
22
|
+
state.activePlan = advResult.plan;
|
|
23
|
+
state.engineOptions.plan = state.activePlan;
|
|
24
|
+
}
|
|
25
|
+
const progress = getPlanProgress(state.activePlan);
|
|
26
|
+
callbacks.onPlanProgress?.(progress);
|
|
27
|
+
if (progress.completed === progress.total) {
|
|
28
|
+
callbacks.onPlanComplete?.();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function extractScope(args) {
|
|
33
|
+
if (typeof args.path === "string") return args.path;
|
|
34
|
+
if (typeof args.file_path === "string") return args.file_path;
|
|
35
|
+
if (typeof args.filename === "string") return args.filename;
|
|
36
|
+
if (typeof args.url === "string") return args.url;
|
|
37
|
+
if (typeof args.command === "string") return args.command;
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
function buildEngineOptions(options, plan) {
|
|
41
|
+
return {
|
|
42
|
+
trace: options.trace ?? false,
|
|
43
|
+
level: options.level,
|
|
44
|
+
plan: plan ?? options.plan
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function defaultBlockMessage(verdict) {
|
|
48
|
+
return `Action blocked by governance policy: ${verdict.reason ?? "rule violation"}. Rule: ${verdict.ruleId ?? "unknown"}.`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
GovernanceBlockedError,
|
|
53
|
+
trackPlanProgress,
|
|
54
|
+
extractScope,
|
|
55
|
+
buildEngineOptions,
|
|
56
|
+
defaultBlockMessage
|
|
57
|
+
};
|
|
@@ -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
|
+
};
|