@neuroverseos/governance 0.3.3 → 0.3.4

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 (34) hide show
  1. package/dist/adapters/autoresearch.d.cts +1 -1
  2. package/dist/adapters/autoresearch.d.ts +1 -1
  3. package/dist/adapters/deep-agents.d.cts +2 -2
  4. package/dist/adapters/deep-agents.d.ts +2 -2
  5. package/dist/adapters/express.d.cts +1 -1
  6. package/dist/adapters/express.d.ts +1 -1
  7. package/dist/adapters/index.d.cts +2 -2
  8. package/dist/adapters/index.d.ts +2 -2
  9. package/dist/adapters/langchain.d.cts +2 -2
  10. package/dist/adapters/langchain.d.ts +2 -2
  11. package/dist/adapters/openai.d.cts +2 -2
  12. package/dist/adapters/openai.d.ts +2 -2
  13. package/dist/adapters/openclaw.d.cts +2 -2
  14. package/dist/adapters/openclaw.d.ts +2 -2
  15. package/dist/{build-QKOBBC23.js → build-ZHPMX5AZ.js} +3 -2
  16. package/dist/chunk-FMSTRBBS.js +17 -0
  17. package/dist/{chunk-Q6O7ZLO2.js → chunk-INWQHLPS.js} +1 -16
  18. package/dist/chunk-VXHSMA3I.js +166 -0
  19. package/dist/{chunk-6S5CFQXY.js → chunk-YEKMVDWK.js} +3 -3
  20. package/dist/cli/neuroverse.cjs +191 -3
  21. package/dist/cli/neuroverse.js +4 -4
  22. package/dist/{configure-ai-6TZ3MCSI.js → configure-ai-5MP5DWTT.js} +5 -3
  23. package/dist/{derive-FJZVIPUZ.js → derive-LMDUTXDD.js} +3 -2
  24. package/dist/{guard-AJCCGZMF.js → guard-AEEJNWLD.js} +39 -5
  25. package/dist/{guard-contract-DqFcTScd.d.cts → guard-contract-B7lplwm9.d.cts} +16 -0
  26. package/dist/{guard-contract-DqFcTScd.d.ts → guard-contract-B7lplwm9.d.ts} +16 -0
  27. package/dist/index.cjs +195 -48
  28. package/dist/index.d.cts +221 -66
  29. package/dist/index.d.ts +221 -66
  30. package/dist/index.js +11 -4
  31. package/dist/{shared-B8dvUUD8.d.cts → shared-C_zpdvBm.d.cts} +1 -1
  32. package/dist/{shared-Dr5Wiay8.d.ts → shared-Cf7yxx4-.d.ts} +1 -1
  33. package/package.json +1 -1
  34. package/dist/chunk-MH7BT4VH.js +0 -15
@@ -2,11 +2,12 @@ import {
2
2
  DeriveInputError,
3
3
  DeriveProviderError,
4
4
  deriveWorld
5
- } from "./chunk-6S5CFQXY.js";
5
+ } from "./chunk-YEKMVDWK.js";
6
6
  import {
7
7
  DERIVE_EXIT_CODES
8
- } from "./chunk-Q6O7ZLO2.js";
8
+ } from "./chunk-FMSTRBBS.js";
9
9
  import "./chunk-OT6PXH54.js";
10
+ import "./chunk-INWQHLPS.js";
10
11
  import "./chunk-7P3S7MAY.js";
11
12
  import "./chunk-EMQDLDAF.js";
12
13
  import "./chunk-PVTQQS3Y.js";
@@ -2,8 +2,10 @@ import {
2
2
  readStdin
3
3
  } from "./chunk-BQZMOEML.js";
4
4
  import {
5
- GUARD_EXIT_CODES
6
- } from "./chunk-MH7BT4VH.js";
5
+ GUARD_EXIT_CODES,
6
+ evaluateGuardWithAI
7
+ } from "./chunk-VXHSMA3I.js";
8
+ import "./chunk-INWQHLPS.js";
7
9
  import {
8
10
  describeActiveWorld,
9
11
  resolveWorldPath
@@ -22,12 +24,15 @@ function parseArgs(argv) {
22
24
  let worldPath = "";
23
25
  let trace = false;
24
26
  let level;
27
+ let aiClassify = false;
25
28
  for (let i = 0; i < argv.length; i++) {
26
29
  const arg = argv[i];
27
30
  if (arg === "--world" && i + 1 < argv.length) {
28
31
  worldPath = argv[++i];
29
32
  } else if (arg === "--trace") {
30
33
  trace = true;
34
+ } else if (arg === "--ai-classify") {
35
+ aiClassify = true;
31
36
  } else if (arg === "--level" && i + 1 < argv.length) {
32
37
  const val = argv[++i];
33
38
  if (val === "basic" || val === "standard" || val === "strict") {
@@ -37,7 +42,7 @@ function parseArgs(argv) {
37
42
  }
38
43
  }
39
44
  }
40
- return { worldPath, trace, level };
45
+ return { worldPath, trace, level, aiClassify };
41
46
  }
42
47
  async function main(argv = process.argv.slice(2)) {
43
48
  try {
@@ -76,8 +81,27 @@ async function main(argv = process.argv.slice(2)) {
76
81
  process.exit(GUARD_EXIT_CODES.ERROR);
77
82
  }
78
83
  const world = await loadWorld(worldPath);
79
- const options = { trace: args.trace, level: args.level };
80
- const verdict = evaluateGuard(event, world, options);
84
+ let verdict;
85
+ if (args.aiClassify) {
86
+ const aiConfig = resolveAIConfig();
87
+ if (!aiConfig) {
88
+ const errorResult = {
89
+ error: "AI classification requires an API key. Set NEUROVERSE_AI_API_KEY and optionally NEUROVERSE_AI_MODEL, NEUROVERSE_AI_ENDPOINT."
90
+ };
91
+ process.stdout.write(JSON.stringify(errorResult, null, 2) + "\n");
92
+ process.exit(GUARD_EXIT_CODES.ERROR);
93
+ }
94
+ process.stderr.write("AI intent classification enabled\n");
95
+ verdict = await evaluateGuardWithAI(event, world, {
96
+ trace: args.trace,
97
+ level: args.level,
98
+ ai: aiConfig,
99
+ contentFields: event.contentFields
100
+ });
101
+ } else {
102
+ const options = { trace: args.trace, level: args.level };
103
+ verdict = evaluateGuard(event, world, options);
104
+ }
81
105
  process.stdout.write(JSON.stringify(verdict, null, 2) + "\n");
82
106
  const exitCode = GUARD_EXIT_CODES[verdict.status];
83
107
  process.exit(exitCode);
@@ -87,6 +111,16 @@ async function main(argv = process.argv.slice(2)) {
87
111
  process.exit(GUARD_EXIT_CODES.ERROR);
88
112
  }
89
113
  }
114
+ function resolveAIConfig() {
115
+ const apiKey = process.env.NEUROVERSE_AI_API_KEY;
116
+ if (!apiKey) return null;
117
+ return {
118
+ provider: "openai",
119
+ model: process.env.NEUROVERSE_AI_MODEL ?? "gpt-4o-mini",
120
+ apiKey,
121
+ endpoint: process.env.NEUROVERSE_AI_ENDPOINT ?? null
122
+ };
123
+ }
90
124
  export {
91
125
  main
92
126
  };
@@ -536,6 +536,22 @@ interface GuardEvent {
536
536
  args?: Record<string, unknown>;
537
537
  /** Environment context (e.g. "development", "production") */
538
538
  environment?: string;
539
+ /**
540
+ * Structured content fields for AI intent classification.
541
+ *
542
+ * When present, enables separation of customer input from AI output
543
+ * so the guard can distinguish "customer demands escalation" from
544
+ * "AI politely says 'I am escalating your request'".
545
+ *
546
+ * Used by evaluateGuardWithAI() — ignored by the sync evaluateGuard().
547
+ */
548
+ contentFields?: {
549
+ customer_input?: string;
550
+ draft_reply?: string;
551
+ tool?: string;
552
+ context?: string;
553
+ raw?: string;
554
+ };
539
555
  }
540
556
  type GuardStatus = 'ALLOW' | 'BLOCK' | 'PAUSE' | 'MODIFY' | 'PENALIZE' | 'REWARD' | 'NEUTRAL';
541
557
  /**
@@ -536,6 +536,22 @@ interface GuardEvent {
536
536
  args?: Record<string, unknown>;
537
537
  /** Environment context (e.g. "development", "production") */
538
538
  environment?: string;
539
+ /**
540
+ * Structured content fields for AI intent classification.
541
+ *
542
+ * When present, enables separation of customer input from AI output
543
+ * so the guard can distinguish "customer demands escalation" from
544
+ * "AI politely says 'I am escalating your request'".
545
+ *
546
+ * Used by evaluateGuardWithAI() — ignored by the sync evaluateGuard().
547
+ */
548
+ contentFields?: {
549
+ customer_input?: string;
550
+ draft_reply?: string;
551
+ tool?: string;
552
+ context?: string;
553
+ raw?: string;
554
+ };
539
555
  }
540
556
  type GuardStatus = 'ALLOW' | 'BLOCK' | 'PAUSE' | 'MODIFY' | 'PENALIZE' | 'REWARD' | 'NEUTRAL';
541
557
  /**
package/dist/index.cjs CHANGED
@@ -857,6 +857,7 @@ __export(index_exports, {
857
857
  buildPlanCheck: () => buildPlanCheck,
858
858
  classifyAdaptation: () => classifyAdaptation,
859
859
  classifyIntent: () => classifyIntent,
860
+ classifyIntentWithAI: () => classifyIntentWithAI,
860
861
  createAgentState: () => createAgentState,
861
862
  createGovernanceEngine: () => createGovernanceEngine,
862
863
  createGovernor: () => createGovernor,
@@ -866,9 +867,11 @@ __export(index_exports, {
866
867
  emitWorldDefinition: () => emitWorldDefinition,
867
868
  evaluateCondition: () => evaluateCondition,
868
869
  evaluateGuard: () => evaluateGuard,
870
+ evaluateGuardWithAI: () => evaluateGuardWithAI,
869
871
  evaluatePlan: () => evaluatePlan,
870
872
  eventToAllowlistKey: () => eventToAllowlistKey,
871
873
  explainWorld: () => explainWorld,
874
+ extractContentFields: () => extractContentFields,
872
875
  extractWorldMarkdown: () => extractWorldMarkdown,
873
876
  formatVerdict: () => formatVerdict,
874
877
  formatVerdictOneLine: () => formatVerdictOneLine,
@@ -1975,6 +1978,191 @@ function buildVerdict(status, reason, ruleId, warning, world, level, invariantCh
1975
1978
  return verdict;
1976
1979
  }
1977
1980
 
1981
+ // src/providers/ai-provider.ts
1982
+ var ChatCompletionsProvider = class {
1983
+ model;
1984
+ apiKey;
1985
+ endpoint;
1986
+ constructor(config) {
1987
+ this.model = config.model;
1988
+ this.apiKey = config.apiKey;
1989
+ this.endpoint = config.endpoint ?? "https://api.openai.com/v1/chat/completions";
1990
+ }
1991
+ async complete(systemPrompt, userPrompt) {
1992
+ const body = {
1993
+ model: this.model,
1994
+ messages: [
1995
+ { role: "system", content: systemPrompt },
1996
+ { role: "user", content: userPrompt }
1997
+ ],
1998
+ temperature: 0.2,
1999
+ max_tokens: 16384
2000
+ };
2001
+ const response = await fetch(this.endpoint, {
2002
+ method: "POST",
2003
+ headers: {
2004
+ "Content-Type": "application/json",
2005
+ "Authorization": `Bearer ${this.apiKey}`
2006
+ },
2007
+ body: JSON.stringify(body)
2008
+ });
2009
+ if (!response.ok) {
2010
+ const text = await response.text().catch(() => "");
2011
+ throw new Error(`Provider returned ${response.status}: ${text.slice(0, 200)}`);
2012
+ }
2013
+ const data = await response.json();
2014
+ const content = data?.choices?.[0]?.message?.content;
2015
+ if (!content) {
2016
+ throw new Error("Provider returned empty response \u2014 no content in choices[0].message.content");
2017
+ }
2018
+ return content;
2019
+ }
2020
+ };
2021
+ function createProvider(config) {
2022
+ return new ChatCompletionsProvider(config);
2023
+ }
2024
+
2025
+ // src/engine/intent-classifier.ts
2026
+ function buildSystemPrompt(knownIntents) {
2027
+ let prompt = `You are an intent classifier for an AI governance system. Your job is to analyze structured content fields and produce a clean, semantic intent label.
2028
+
2029
+ CRITICAL RULES:
2030
+ 1. Distinguish WHO is performing the action. If the AI agent says "I am escalating your request", that is the AI being polite \u2014 NOT the customer demanding escalation.
2031
+ 2. Focus on the CUSTOMER'S actual intent, not the AI's response language.
2032
+ 3. Return a short, snake_case intent label (e.g. "shipping_inquiry", "complaint_escalation", "password_reset").
2033
+ 4. Assess the actor: who initiated this action? The customer, the AI agent, or the system?
2034
+
2035
+ You must respond with ONLY valid JSON in this exact format:
2036
+ {"intent": "<label>", "confidence": <0-1>, "actor": "<customer|ai_agent|system|unknown>", "reasoning": "<one sentence>"}`;
2037
+ if (knownIntents && knownIntents.length > 0) {
2038
+ prompt += `
2039
+
2040
+ Preferred intent labels (use these when applicable, but you may create new ones if none fit):
2041
+ ${knownIntents.map((i) => `- ${i}`).join("\n")}`;
2042
+ }
2043
+ return prompt;
2044
+ }
2045
+ function buildUserPrompt(fields) {
2046
+ const parts = [];
2047
+ if (fields.customer_input) {
2048
+ parts.push(`CUSTOMER INPUT:
2049
+ ${fields.customer_input}`);
2050
+ }
2051
+ if (fields.draft_reply) {
2052
+ parts.push(`AI DRAFT REPLY:
2053
+ ${fields.draft_reply}`);
2054
+ }
2055
+ if (fields.tool) {
2056
+ parts.push(`TOOL: ${fields.tool}`);
2057
+ }
2058
+ if (fields.context) {
2059
+ parts.push(`CONTEXT: ${fields.context}`);
2060
+ }
2061
+ if (fields.raw && parts.length === 0) {
2062
+ parts.push(`RAW TEXT:
2063
+ ${fields.raw}`);
2064
+ }
2065
+ return parts.join("\n\n");
2066
+ }
2067
+ async function classifyIntentWithAI(fields, options) {
2068
+ const provider = createProvider(options.ai);
2069
+ const systemPrompt = buildSystemPrompt(options.knownIntents);
2070
+ const userPrompt = buildUserPrompt(fields);
2071
+ const response = await provider.complete(systemPrompt, userPrompt);
2072
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
2073
+ if (!jsonMatch) {
2074
+ throw new Error("Intent classifier returned non-JSON response");
2075
+ }
2076
+ const parsed = JSON.parse(jsonMatch[0]);
2077
+ if (!parsed.intent || typeof parsed.intent !== "string") {
2078
+ throw new Error("Intent classifier returned invalid intent label");
2079
+ }
2080
+ return {
2081
+ intent: parsed.intent,
2082
+ confidence: typeof parsed.confidence === "number" ? parsed.confidence : 0.5,
2083
+ actor: ["customer", "ai_agent", "system", "unknown"].includes(
2084
+ parsed.actor
2085
+ ) ? parsed.actor : "unknown",
2086
+ reasoning: parsed.reasoning ?? ""
2087
+ };
2088
+ }
2089
+ var OUTPUT_CONTENT_FIELDS = /* @__PURE__ */ new Set([
2090
+ "draft_reply",
2091
+ "content",
2092
+ "body",
2093
+ "message",
2094
+ "text",
2095
+ "reply",
2096
+ "response",
2097
+ "output",
2098
+ "html",
2099
+ "template"
2100
+ ]);
2101
+ function extractContentFields(intent, args) {
2102
+ if (!args) {
2103
+ return { raw: intent };
2104
+ }
2105
+ const fields = {};
2106
+ let hasStructuredContent = false;
2107
+ for (const [key, value] of Object.entries(args)) {
2108
+ if (typeof value !== "string") continue;
2109
+ const lowerKey = key.toLowerCase();
2110
+ if (OUTPUT_CONTENT_FIELDS.has(lowerKey)) {
2111
+ fields.draft_reply = fields.draft_reply ? `${fields.draft_reply}
2112
+
2113
+ ${value}` : value;
2114
+ hasStructuredContent = true;
2115
+ }
2116
+ }
2117
+ if (hasStructuredContent) {
2118
+ fields.customer_input = intent;
2119
+ } else {
2120
+ fields.raw = intent;
2121
+ }
2122
+ return fields;
2123
+ }
2124
+
2125
+ // src/engine/ai-guard.ts
2126
+ async function evaluateGuardWithAI(event, world, options = {}) {
2127
+ if (!options.ai) {
2128
+ const verdict = evaluateGuard(event, world, options);
2129
+ verdict.intent_source = "raw";
2130
+ return verdict;
2131
+ }
2132
+ const fallbackOnError = options.fallbackOnError ?? true;
2133
+ const originalIntent = event.intent;
2134
+ const knownIntents = options.knownIntents ?? extractKnownIntents(world);
2135
+ const contentFields = options.contentFields ?? extractContentFields(event.intent, event.args);
2136
+ try {
2137
+ const classification = await classifyIntentWithAI(contentFields, {
2138
+ ai: options.ai,
2139
+ knownIntents
2140
+ });
2141
+ const classifiedEvent = {
2142
+ ...event,
2143
+ intent: classification.intent
2144
+ };
2145
+ const verdict = evaluateGuard(classifiedEvent, world, options);
2146
+ verdict.intent_source = "ai";
2147
+ verdict.classification = classification;
2148
+ verdict.originalIntent = originalIntent;
2149
+ return verdict;
2150
+ } catch (err) {
2151
+ if (fallbackOnError) {
2152
+ const verdict = evaluateGuard(event, world, options);
2153
+ verdict.intent_source = "fallback";
2154
+ verdict.originalIntent = originalIntent;
2155
+ return verdict;
2156
+ }
2157
+ throw err;
2158
+ }
2159
+ }
2160
+ function extractKnownIntents(world) {
2161
+ const vocab = world.guards?.intent_vocabulary;
2162
+ if (!vocab) return [];
2163
+ return Object.keys(vocab);
2164
+ }
2165
+
1978
2166
  // src/contracts/guard-contract.ts
1979
2167
  var GUARD_EXIT_CODES = {
1980
2168
  ALLOW: 0,
@@ -5962,7 +6150,7 @@ H2 sub-sections defining computed outcomes:
5962
6150
  - label: Human Label
5963
6151
  - primary: true (for the main metric)
5964
6152
  `.trim();
5965
- async function buildSystemPrompt() {
6153
+ async function buildSystemPrompt2() {
5966
6154
  const derivationWorld = await loadDerivationWorld();
5967
6155
  const invariantLines = derivationWorld.split("\n").filter((l) => l.trim().startsWith("- `")).map((l) => {
5968
6156
  const match = l.match(/^-\s+`([^`]+)`\s*[—–-]\s*(.+?)(?:\s*\([^)]+\))?\s*$/);
@@ -6012,7 +6200,7 @@ EFFECTS: Every effect MUST use a recognized operator:
6012
6200
  CORRECT: Then score *= 0.50, active = false
6013
6201
  WRONG: Then score decreases by half`;
6014
6202
  }
6015
- function buildUserPrompt(concatenatedMarkdown) {
6203
+ function buildUserPrompt2(concatenatedMarkdown) {
6016
6204
  return `Synthesize a valid .nv-world.md under DerivationWorld constraints from the following source material.
6017
6205
 
6018
6206
  Output ONLY the .nv-world.md content. Start with the --- frontmatter delimiter.
@@ -6022,50 +6210,6 @@ Source material:
6022
6210
  ${concatenatedMarkdown}`;
6023
6211
  }
6024
6212
 
6025
- // src/providers/ai-provider.ts
6026
- var ChatCompletionsProvider = class {
6027
- model;
6028
- apiKey;
6029
- endpoint;
6030
- constructor(config) {
6031
- this.model = config.model;
6032
- this.apiKey = config.apiKey;
6033
- this.endpoint = config.endpoint ?? "https://api.openai.com/v1/chat/completions";
6034
- }
6035
- async complete(systemPrompt, userPrompt) {
6036
- const body = {
6037
- model: this.model,
6038
- messages: [
6039
- { role: "system", content: systemPrompt },
6040
- { role: "user", content: userPrompt }
6041
- ],
6042
- temperature: 0.2,
6043
- max_tokens: 16384
6044
- };
6045
- const response = await fetch(this.endpoint, {
6046
- method: "POST",
6047
- headers: {
6048
- "Content-Type": "application/json",
6049
- "Authorization": `Bearer ${this.apiKey}`
6050
- },
6051
- body: JSON.stringify(body)
6052
- });
6053
- if (!response.ok) {
6054
- const text = await response.text().catch(() => "");
6055
- throw new Error(`Provider returned ${response.status}: ${text.slice(0, 200)}`);
6056
- }
6057
- const data = await response.json();
6058
- const content = data?.choices?.[0]?.message?.content;
6059
- if (!content) {
6060
- throw new Error("Provider returned empty response \u2014 no content in choices[0].message.content");
6061
- }
6062
- return content;
6063
- }
6064
- };
6065
- function createProvider(config) {
6066
- return new ChatCompletionsProvider(config);
6067
- }
6068
-
6069
6213
  // src/providers/config-manager.ts
6070
6214
  var import_promises2 = require("fs/promises");
6071
6215
  var import_path4 = require("path");
@@ -6168,8 +6312,8 @@ async function deriveWorld(options) {
6168
6312
  throw new DeriveInputError("No markdown files found in input path");
6169
6313
  }
6170
6314
  const concatenated = concatenateSources(sources);
6171
- const systemPrompt = await buildSystemPrompt();
6172
- const userPrompt = buildUserPrompt(concatenated);
6315
+ const systemPrompt = await buildSystemPrompt2();
6316
+ const userPrompt = buildUserPrompt2(concatenated);
6173
6317
  if (options.dryRun) {
6174
6318
  return {
6175
6319
  result: {
@@ -7537,6 +7681,7 @@ function handleCreateCapsule(body) {
7537
7681
  buildPlanCheck,
7538
7682
  classifyAdaptation,
7539
7683
  classifyIntent,
7684
+ classifyIntentWithAI,
7540
7685
  createAgentState,
7541
7686
  createGovernanceEngine,
7542
7687
  createGovernor,
@@ -7546,9 +7691,11 @@ function handleCreateCapsule(body) {
7546
7691
  emitWorldDefinition,
7547
7692
  evaluateCondition,
7548
7693
  evaluateGuard,
7694
+ evaluateGuardWithAI,
7549
7695
  evaluatePlan,
7550
7696
  eventToAllowlistKey,
7551
7697
  explainWorld,
7698
+ extractContentFields,
7552
7699
  extractWorldMarkdown,
7553
7700
  formatVerdict,
7554
7701
  formatVerdictOneLine,