@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,61 @@
|
|
|
1
|
+
// src/providers/config-manager.ts
|
|
2
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
function getConfigDir() {
|
|
6
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
7
|
+
if (xdg) return join(xdg, "neuroverse");
|
|
8
|
+
return join(homedir(), ".neuroverse");
|
|
9
|
+
}
|
|
10
|
+
function getConfigPath() {
|
|
11
|
+
return join(getConfigDir(), "config.json");
|
|
12
|
+
}
|
|
13
|
+
async function loadConfig() {
|
|
14
|
+
try {
|
|
15
|
+
const raw = await readFile(getConfigPath(), "utf-8");
|
|
16
|
+
const parsed = JSON.parse(raw);
|
|
17
|
+
if (!parsed.provider || !parsed.model || !parsed.apiKey) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
provider: parsed.provider,
|
|
22
|
+
model: parsed.model,
|
|
23
|
+
apiKey: parsed.apiKey,
|
|
24
|
+
endpoint: parsed.endpoint ?? null
|
|
25
|
+
};
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function saveConfig(config) {
|
|
31
|
+
const dir = getConfigDir();
|
|
32
|
+
await mkdir(dir, { recursive: true });
|
|
33
|
+
const configPath = getConfigPath();
|
|
34
|
+
const content = JSON.stringify(
|
|
35
|
+
{
|
|
36
|
+
provider: config.provider,
|
|
37
|
+
model: config.model,
|
|
38
|
+
apiKey: config.apiKey,
|
|
39
|
+
endpoint: config.endpoint
|
|
40
|
+
},
|
|
41
|
+
null,
|
|
42
|
+
2
|
|
43
|
+
);
|
|
44
|
+
await writeFile(configPath, content, { mode: 384 });
|
|
45
|
+
await chmod(configPath, 384);
|
|
46
|
+
}
|
|
47
|
+
function redactConfig(config) {
|
|
48
|
+
return {
|
|
49
|
+
provider: config.provider,
|
|
50
|
+
model: config.model,
|
|
51
|
+
apiKey: config.apiKey ? `${config.apiKey.slice(0, 4)}...${config.apiKey.slice(-4)}` : "(not set)",
|
|
52
|
+
endpoint: config.endpoint
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
getConfigPath,
|
|
58
|
+
loadConfig,
|
|
59
|
+
saveConfig,
|
|
60
|
+
redactConfig
|
|
61
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// src/engine/bootstrap-emitter.ts
|
|
2
|
+
var GATE_DEFAULTS = {
|
|
3
|
+
THRIVING: { color: "#0f6b3a", icon: "\u2726" },
|
|
4
|
+
STABLE: { color: "#1856b8", icon: "\u25CF" },
|
|
5
|
+
COMPRESSED: { color: "#a16207", icon: "\u25B2" },
|
|
6
|
+
CRITICAL: { color: "#b91c1c", icon: "\u26A0" },
|
|
7
|
+
MODEL_COLLAPSES: { color: "#7f1d1d", icon: "\u2715" }
|
|
8
|
+
};
|
|
9
|
+
function emitWorldDefinition(parsed) {
|
|
10
|
+
const issues = [];
|
|
11
|
+
const fm = parsed.frontmatter;
|
|
12
|
+
const defaultProfile = fm.default_profile ?? parsed.assumptions[0]?.id ?? "baseline";
|
|
13
|
+
const altProfile = fm.alternative_profile ?? parsed.assumptions[1]?.id ?? "alternative";
|
|
14
|
+
const world = {
|
|
15
|
+
world_id: fm.world_id,
|
|
16
|
+
name: fm.name,
|
|
17
|
+
thesis: parsed.thesis,
|
|
18
|
+
version: fm.version ?? "1.0.0",
|
|
19
|
+
runtime_mode: fm.runtime_mode ?? "SIMULATION",
|
|
20
|
+
default_assumption_profile: defaultProfile,
|
|
21
|
+
default_alternative_profile: altProfile,
|
|
22
|
+
modules: parsed.rules.map((r) => r.id),
|
|
23
|
+
players: {
|
|
24
|
+
thinking_space: true,
|
|
25
|
+
experience_space: true,
|
|
26
|
+
action_space: true
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const invariants = parsed.invariants.map((inv) => ({
|
|
30
|
+
id: inv.id,
|
|
31
|
+
label: inv.label,
|
|
32
|
+
enforcement: inv.enforcement === "prompt" ? "prompt" : "structural",
|
|
33
|
+
mutable: false
|
|
34
|
+
}));
|
|
35
|
+
const profiles = {};
|
|
36
|
+
const parameterDefinitions = {};
|
|
37
|
+
for (let i = 0; i < parsed.assumptions.length; i++) {
|
|
38
|
+
const profile = parsed.assumptions[i];
|
|
39
|
+
const params = {};
|
|
40
|
+
for (const [key, val] of Object.entries(profile.parameters)) {
|
|
41
|
+
params[key] = String(val);
|
|
42
|
+
if (!parameterDefinitions[key]) {
|
|
43
|
+
parameterDefinitions[key] = {
|
|
44
|
+
type: typeof val === "boolean" ? "boolean" : typeof val === "number" ? "number" : "enum",
|
|
45
|
+
label: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
46
|
+
description: `Parameter: ${key}`
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
profiles[profile.id] = {
|
|
51
|
+
name: profile.name,
|
|
52
|
+
description: profile.description,
|
|
53
|
+
is_default_baseline: i === 0 || profile.id === defaultProfile,
|
|
54
|
+
is_default_alternative: i === 1 || profile.id === altProfile,
|
|
55
|
+
parameters: params
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const assumptions = { profiles, parameter_definitions: parameterDefinitions };
|
|
59
|
+
const variables = {};
|
|
60
|
+
for (const v of parsed.stateVariables) {
|
|
61
|
+
const stateVar = {
|
|
62
|
+
type: v.type,
|
|
63
|
+
default: v.default,
|
|
64
|
+
mutable: true,
|
|
65
|
+
label: v.label,
|
|
66
|
+
description: v.description
|
|
67
|
+
};
|
|
68
|
+
if (v.type === "number") {
|
|
69
|
+
if (v.min !== void 0) stateVar.min = v.min;
|
|
70
|
+
if (v.max !== void 0) stateVar.max = v.max;
|
|
71
|
+
if (v.step !== void 0) stateVar.step = v.step;
|
|
72
|
+
}
|
|
73
|
+
if (v.type === "enum" && v.options) {
|
|
74
|
+
stateVar.options = v.options;
|
|
75
|
+
}
|
|
76
|
+
variables[v.id] = stateVar;
|
|
77
|
+
}
|
|
78
|
+
const stateSchema = { variables, presets: {} };
|
|
79
|
+
const rules = parsed.rules.map((r) => {
|
|
80
|
+
const triggers = r.triggers.map((t) => ({
|
|
81
|
+
field: t.field,
|
|
82
|
+
operator: t.operator,
|
|
83
|
+
value: t.value,
|
|
84
|
+
source: t.source
|
|
85
|
+
}));
|
|
86
|
+
const effects = r.effects.map((e) => ({
|
|
87
|
+
target: e.target,
|
|
88
|
+
operation: e.operation,
|
|
89
|
+
value: e.value
|
|
90
|
+
}));
|
|
91
|
+
let collapse_check;
|
|
92
|
+
if (r.collapse_check) {
|
|
93
|
+
collapse_check = {
|
|
94
|
+
field: r.collapse_check.field,
|
|
95
|
+
operator: r.collapse_check.operator,
|
|
96
|
+
value: r.collapse_check.value,
|
|
97
|
+
result: "MODEL_COLLAPSES"
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const causal_translation = r.causal_translation ?? {
|
|
101
|
+
trigger_text: "",
|
|
102
|
+
rule_text: "",
|
|
103
|
+
shift_text: "",
|
|
104
|
+
effect_text: ""
|
|
105
|
+
};
|
|
106
|
+
const rule = {
|
|
107
|
+
id: r.id,
|
|
108
|
+
severity: r.severity,
|
|
109
|
+
label: r.label,
|
|
110
|
+
description: r.description ?? r.label,
|
|
111
|
+
order: r.order,
|
|
112
|
+
triggers,
|
|
113
|
+
effects: effects.length > 0 ? effects : void 0,
|
|
114
|
+
collapse_check,
|
|
115
|
+
causal_translation
|
|
116
|
+
};
|
|
117
|
+
return rule;
|
|
118
|
+
});
|
|
119
|
+
const viabilityClassification = parsed.gates.map((g) => {
|
|
120
|
+
const defaults = GATE_DEFAULTS[g.status] ?? { color: "#5c5a52", icon: "\u25CF" };
|
|
121
|
+
return {
|
|
122
|
+
status: g.status,
|
|
123
|
+
field: g.field,
|
|
124
|
+
operator: g.operator,
|
|
125
|
+
value: g.value,
|
|
126
|
+
color: defaults.color,
|
|
127
|
+
icon: defaults.icon
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
const gates = {
|
|
131
|
+
viability_classification: viabilityClassification,
|
|
132
|
+
structural_override: {
|
|
133
|
+
description: "Rules with severity=structural and triggered collapse_check force MODEL_COLLAPSES regardless of final margin.",
|
|
134
|
+
enforcement: "mandatory"
|
|
135
|
+
},
|
|
136
|
+
sustainability_threshold: 0.1,
|
|
137
|
+
collapse_visual: {
|
|
138
|
+
background: "#1c1917",
|
|
139
|
+
text: "#fef2f2",
|
|
140
|
+
border: "#b91c1c",
|
|
141
|
+
label: "Structural Failure"
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const computedOutcomes = parsed.outcomes.map((o) => {
|
|
145
|
+
const outcome = {
|
|
146
|
+
id: o.id,
|
|
147
|
+
type: o.type,
|
|
148
|
+
label: o.label,
|
|
149
|
+
show_in_comparison: true
|
|
150
|
+
};
|
|
151
|
+
if (o.range) outcome.range = o.range;
|
|
152
|
+
if (o.display) outcome.display_as = o.display;
|
|
153
|
+
if (o.primary) outcome.primary = o.primary;
|
|
154
|
+
if (o.assignment) outcome.assignment = o.assignment;
|
|
155
|
+
return outcome;
|
|
156
|
+
});
|
|
157
|
+
const outcomes = {
|
|
158
|
+
computed_outcomes: computedOutcomes,
|
|
159
|
+
comparison_layout: {
|
|
160
|
+
primary_card: computedOutcomes.find((o) => o.primary)?.id ?? computedOutcomes[0]?.id ?? "",
|
|
161
|
+
status_badge: "viability_status",
|
|
162
|
+
structural_indicators: rules.filter((r) => r.severity === "structural").map((r) => r.id)
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const metadata = {
|
|
166
|
+
format_version: "1.0.0",
|
|
167
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
168
|
+
last_modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
169
|
+
authoring_method: "manual-authoring"
|
|
170
|
+
};
|
|
171
|
+
const worldDefinition = {
|
|
172
|
+
world,
|
|
173
|
+
invariants,
|
|
174
|
+
assumptions,
|
|
175
|
+
stateSchema,
|
|
176
|
+
rules,
|
|
177
|
+
gates,
|
|
178
|
+
outcomes,
|
|
179
|
+
metadata
|
|
180
|
+
};
|
|
181
|
+
return { world: worldDefinition, issues };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export {
|
|
185
|
+
emitWorldDefinition
|
|
186
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/providers/ai-provider.ts
|
|
2
|
+
var ChatCompletionsProvider = class {
|
|
3
|
+
model;
|
|
4
|
+
apiKey;
|
|
5
|
+
endpoint;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.model = config.model;
|
|
8
|
+
this.apiKey = config.apiKey;
|
|
9
|
+
this.endpoint = config.endpoint ?? "https://api.openai.com/v1/chat/completions";
|
|
10
|
+
}
|
|
11
|
+
async complete(systemPrompt, userPrompt) {
|
|
12
|
+
const body = {
|
|
13
|
+
model: this.model,
|
|
14
|
+
messages: [
|
|
15
|
+
{ role: "system", content: systemPrompt },
|
|
16
|
+
{ role: "user", content: userPrompt }
|
|
17
|
+
],
|
|
18
|
+
temperature: 0.2,
|
|
19
|
+
max_tokens: 16384
|
|
20
|
+
};
|
|
21
|
+
const response = await fetch(this.endpoint, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(body)
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const text = await response.text().catch(() => "");
|
|
31
|
+
throw new Error(`Provider returned ${response.status}: ${text.slice(0, 200)}`);
|
|
32
|
+
}
|
|
33
|
+
const data = await response.json();
|
|
34
|
+
const content = data?.choices?.[0]?.message?.content;
|
|
35
|
+
if (!content) {
|
|
36
|
+
throw new Error("Provider returned empty response \u2014 no content in choices[0].message.content");
|
|
37
|
+
}
|
|
38
|
+
return content;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function createProvider(config) {
|
|
42
|
+
return new ChatCompletionsProvider(config);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/contracts/derive-contract.ts
|
|
46
|
+
var DERIVE_EXIT_CODES = {
|
|
47
|
+
SUCCESS: 0,
|
|
48
|
+
VALIDATION_FAIL: 1,
|
|
49
|
+
INPUT_ERROR: 2,
|
|
50
|
+
PROVIDER_ERROR: 3
|
|
51
|
+
};
|
|
52
|
+
var CONFIGURE_AI_EXIT_CODES = {
|
|
53
|
+
SUCCESS: 0,
|
|
54
|
+
VALIDATION_FAIL: 1,
|
|
55
|
+
ERROR: 3
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
createProvider,
|
|
60
|
+
DERIVE_EXIT_CODES,
|
|
61
|
+
CONFIGURE_AI_EXIT_CODES
|
|
62
|
+
};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// src/engine/text-utils.ts
|
|
2
|
+
function normalizeEventText(event) {
|
|
3
|
+
return [
|
|
4
|
+
event.intent,
|
|
5
|
+
event.tool ?? "",
|
|
6
|
+
event.scope ?? ""
|
|
7
|
+
].join(" ").toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
function extractKeywords(text, minLength = 3) {
|
|
10
|
+
return text.toLowerCase().split(/\s+/).filter((w) => w.length > minLength);
|
|
11
|
+
}
|
|
12
|
+
function matchesAllKeywords(eventText, ruleText) {
|
|
13
|
+
const keywords = extractKeywords(ruleText);
|
|
14
|
+
if (keywords.length === 0) return false;
|
|
15
|
+
return keywords.every((kw) => eventText.includes(kw));
|
|
16
|
+
}
|
|
17
|
+
function matchesKeywordThreshold(eventText, ruleText, threshold = 0.5) {
|
|
18
|
+
const keywords = extractKeywords(ruleText);
|
|
19
|
+
if (keywords.length === 0) return false;
|
|
20
|
+
const matched = keywords.filter((kw) => eventText.includes(kw));
|
|
21
|
+
return matched.length >= Math.ceil(keywords.length * threshold);
|
|
22
|
+
}
|
|
23
|
+
function tokenSimilarity(a, b) {
|
|
24
|
+
const tokensA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
25
|
+
const tokensB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
26
|
+
if (tokensA.size === 0 || tokensB.size === 0) return 0;
|
|
27
|
+
let intersection = 0;
|
|
28
|
+
for (const t of tokensA) {
|
|
29
|
+
if (tokensB.has(t)) intersection++;
|
|
30
|
+
}
|
|
31
|
+
const union = (/* @__PURE__ */ new Set([...tokensA, ...tokensB])).size;
|
|
32
|
+
return union > 0 ? intersection / union : 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/engine/plan-engine.ts
|
|
36
|
+
function keywordMatch(eventText, step) {
|
|
37
|
+
const stepText = [
|
|
38
|
+
step.label,
|
|
39
|
+
step.description ?? "",
|
|
40
|
+
...step.tags ?? []
|
|
41
|
+
].join(" ");
|
|
42
|
+
return matchesKeywordThreshold(eventText, stepText, 0.5);
|
|
43
|
+
}
|
|
44
|
+
function tokenSimilarity2(a, b) {
|
|
45
|
+
return tokenSimilarity(a, b);
|
|
46
|
+
}
|
|
47
|
+
function findMatchingStep(eventText, event, steps) {
|
|
48
|
+
const pendingOrActive = steps.filter((s) => s.status === "pending" || s.status === "active");
|
|
49
|
+
if (pendingOrActive.length === 0) {
|
|
50
|
+
return { matched: null, closest: null, closestScore: 0 };
|
|
51
|
+
}
|
|
52
|
+
for (const step of pendingOrActive) {
|
|
53
|
+
if (keywordMatch(eventText, step)) {
|
|
54
|
+
if (step.tools && event.tool && !step.tools.includes(event.tool)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
return { matched: step, closest: step, closestScore: 1 };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const intentText = [event.intent, event.tool ?? "", event.scope ?? ""].join(" ");
|
|
61
|
+
let bestStep = null;
|
|
62
|
+
let bestScore = 0;
|
|
63
|
+
for (const step of pendingOrActive) {
|
|
64
|
+
const stepText = [step.label, step.description ?? "", ...step.tags ?? []].join(" ");
|
|
65
|
+
const score = tokenSimilarity2(intentText, stepText);
|
|
66
|
+
if (score > bestScore) {
|
|
67
|
+
bestScore = score;
|
|
68
|
+
bestStep = step;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const SIMILARITY_THRESHOLD = 0.35;
|
|
72
|
+
if (bestScore >= SIMILARITY_THRESHOLD && bestStep) {
|
|
73
|
+
if (bestStep.tools && event.tool && !bestStep.tools.includes(event.tool)) {
|
|
74
|
+
return { matched: null, closest: bestStep, closestScore: bestScore };
|
|
75
|
+
}
|
|
76
|
+
return { matched: bestStep, closest: bestStep, closestScore: bestScore };
|
|
77
|
+
}
|
|
78
|
+
return { matched: null, closest: bestStep, closestScore: bestScore };
|
|
79
|
+
}
|
|
80
|
+
function isSequenceValid(step, plan) {
|
|
81
|
+
if (!plan.sequential) return true;
|
|
82
|
+
if (!step.requires || step.requires.length === 0) return true;
|
|
83
|
+
return step.requires.every((reqId) => {
|
|
84
|
+
const reqStep = plan.steps.find((s) => s.id === reqId);
|
|
85
|
+
return reqStep?.status === "completed";
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function checkConstraints(event, eventText, constraints) {
|
|
89
|
+
const checks = [];
|
|
90
|
+
for (const constraint of constraints) {
|
|
91
|
+
if (constraint.type === "approval") {
|
|
92
|
+
if (constraint.trigger && eventText.includes(constraint.trigger.substring(0, 10).toLowerCase())) {
|
|
93
|
+
checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
|
|
94
|
+
return { violated: constraint, checks };
|
|
95
|
+
}
|
|
96
|
+
const keywords = constraint.description.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
|
|
97
|
+
const relevant = keywords.some((kw) => eventText.includes(kw));
|
|
98
|
+
if (relevant) {
|
|
99
|
+
checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
|
|
100
|
+
return { violated: constraint, checks };
|
|
101
|
+
}
|
|
102
|
+
checks.push({ constraintId: constraint.id, passed: true });
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (constraint.type === "scope" && constraint.trigger) {
|
|
106
|
+
const keywords = extractKeywords(constraint.trigger);
|
|
107
|
+
const violated = keywords.length > 0 && keywords.every((kw) => eventText.includes(kw));
|
|
108
|
+
checks.push({
|
|
109
|
+
constraintId: constraint.id,
|
|
110
|
+
passed: !violated,
|
|
111
|
+
reason: violated ? constraint.description : void 0
|
|
112
|
+
});
|
|
113
|
+
if (violated) {
|
|
114
|
+
return { violated: constraint, checks };
|
|
115
|
+
}
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
checks.push({ constraintId: constraint.id, passed: true });
|
|
119
|
+
}
|
|
120
|
+
return { violated: null, checks };
|
|
121
|
+
}
|
|
122
|
+
function getPlanProgress(plan) {
|
|
123
|
+
const completed = plan.steps.filter((s) => s.status === "completed").length;
|
|
124
|
+
const total = plan.steps.length;
|
|
125
|
+
return {
|
|
126
|
+
completed,
|
|
127
|
+
total,
|
|
128
|
+
percentage: total > 0 ? Math.round(completed / total * 100) : 0
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function advancePlan(plan, stepId, evidence) {
|
|
132
|
+
const step = plan.steps.find((s) => s.id === stepId);
|
|
133
|
+
if (!step) {
|
|
134
|
+
return { success: false, reason: `Step "${stepId}" not found in plan.` };
|
|
135
|
+
}
|
|
136
|
+
if (step.status === "completed") {
|
|
137
|
+
return { success: false, reason: `Step "${stepId}" is already completed.` };
|
|
138
|
+
}
|
|
139
|
+
const mode = plan.completion ?? "trust";
|
|
140
|
+
if (mode === "verified" && step.verify) {
|
|
141
|
+
if (!evidence) {
|
|
142
|
+
return {
|
|
143
|
+
success: false,
|
|
144
|
+
reason: `Step "${step.label}" requires evidence (verify: ${step.verify}). Provide evidence to advance.`
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (evidence.type !== step.verify) {
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
reason: `Evidence type "${evidence.type}" does not match required verification "${step.verify}".`
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const updatedPlan = {
|
|
155
|
+
...plan,
|
|
156
|
+
steps: plan.steps.map(
|
|
157
|
+
(s) => s.id === stepId ? { ...s, status: "completed" } : s
|
|
158
|
+
)
|
|
159
|
+
};
|
|
160
|
+
return {
|
|
161
|
+
success: true,
|
|
162
|
+
plan: updatedPlan,
|
|
163
|
+
evidence: evidence ?? void 0
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function evaluatePlan(event, plan) {
|
|
167
|
+
const progress = getPlanProgress(plan);
|
|
168
|
+
if (plan.expires_at) {
|
|
169
|
+
const expiresAt = new Date(plan.expires_at).getTime();
|
|
170
|
+
if (Date.now() > expiresAt) {
|
|
171
|
+
return {
|
|
172
|
+
allowed: true,
|
|
173
|
+
status: "PLAN_COMPLETE",
|
|
174
|
+
reason: "Plan has expired.",
|
|
175
|
+
progress
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (progress.completed === progress.total) {
|
|
180
|
+
return {
|
|
181
|
+
allowed: true,
|
|
182
|
+
status: "PLAN_COMPLETE",
|
|
183
|
+
reason: "All plan steps are completed.",
|
|
184
|
+
progress
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const eventText = normalizeEventText(event);
|
|
188
|
+
const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
|
|
189
|
+
if (!matched) {
|
|
190
|
+
return {
|
|
191
|
+
allowed: false,
|
|
192
|
+
status: "OFF_PLAN",
|
|
193
|
+
reason: "Action does not match any plan step.",
|
|
194
|
+
closestStep: closest?.label,
|
|
195
|
+
similarityScore: closestScore,
|
|
196
|
+
progress
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
if (!isSequenceValid(matched, plan)) {
|
|
200
|
+
const pendingDeps = (matched.requires ?? []).filter((reqId) => plan.steps.find((s) => s.id === reqId)?.status !== "completed").join(", ");
|
|
201
|
+
return {
|
|
202
|
+
allowed: false,
|
|
203
|
+
status: "OFF_PLAN",
|
|
204
|
+
reason: `Step "${matched.label}" requires completion of: ${pendingDeps}`,
|
|
205
|
+
matchedStep: matched.id,
|
|
206
|
+
progress
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const { violated } = checkConstraints(event, eventText, plan.constraints);
|
|
210
|
+
if (violated) {
|
|
211
|
+
return {
|
|
212
|
+
allowed: false,
|
|
213
|
+
status: "CONSTRAINT_VIOLATED",
|
|
214
|
+
reason: violated.description,
|
|
215
|
+
matchedStep: matched.id,
|
|
216
|
+
progress
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
allowed: true,
|
|
221
|
+
status: "ON_PLAN",
|
|
222
|
+
reason: `Matches step: ${matched.label}`,
|
|
223
|
+
matchedStep: matched.id,
|
|
224
|
+
progress
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function buildPlanCheck(event, plan, verdict) {
|
|
228
|
+
const eventText = normalizeEventText(event);
|
|
229
|
+
const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
|
|
230
|
+
const { checks: constraintChecks } = checkConstraints(event, eventText, plan.constraints);
|
|
231
|
+
const progress = getPlanProgress(plan);
|
|
232
|
+
return {
|
|
233
|
+
planId: plan.plan_id,
|
|
234
|
+
matched: !!matched,
|
|
235
|
+
matchedStepId: matched?.id,
|
|
236
|
+
matchedStepLabel: matched?.label,
|
|
237
|
+
closestStepId: !matched ? closest?.id : void 0,
|
|
238
|
+
closestStepLabel: !matched ? closest?.label : void 0,
|
|
239
|
+
similarityScore: !matched ? closestScore : void 0,
|
|
240
|
+
sequenceValid: matched ? isSequenceValid(matched, plan) : void 0,
|
|
241
|
+
constraintsChecked: constraintChecks,
|
|
242
|
+
progress: { completed: progress.completed, total: progress.total }
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export {
|
|
247
|
+
normalizeEventText,
|
|
248
|
+
matchesAllKeywords,
|
|
249
|
+
getPlanProgress,
|
|
250
|
+
advancePlan,
|
|
251
|
+
evaluatePlan,
|
|
252
|
+
buildPlanCheck
|
|
253
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
var __glob = (map) => (path) => {
|
|
8
|
+
var fn = map[path];
|
|
9
|
+
if (fn) return fn();
|
|
10
|
+
throw new Error("Module not found in bundle: " + path);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
__require,
|
|
15
|
+
__glob
|
|
16
|
+
};
|