@o-lang/olang 1.2.4 → 1.2.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/olang",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "author": "Olalekan Ogundipe <info@workfily.com>",
5
5
  "description": "O-Lang: A governance language for user-directed, rule-enforced agent workflows",
6
6
  "main": "./src/index.js",
@@ -1,4 +1,3 @@
1
- // src/runtime/RuntimeAPI.js
2
1
  const fs = require('fs');
3
2
  const path = require('path');
4
3
 
@@ -332,6 +331,40 @@ class RuntimeAPI {
332
331
  });
333
332
  }
334
333
 
334
+ // -----------------------------
335
+ // āœ… KERNEL-LEVEL LLM HALLUCINATION PREVENTION (ZERO WORKFLOW CHANGES)
336
+ // -----------------------------
337
+ _validateLLMOutput(output, actionContext) {
338
+ if (!output || typeof output !== 'string') return { passed: true };
339
+
340
+ // šŸ”‘ CRITICAL: Extract ONLY allowed capabilities from workflow allowlist
341
+ const allowedCapabilities = Array.from(this.allowedResolvers)
342
+ .filter(name => !name.startsWith('llm-') && name !== 'builtInMathResolver')
343
+ .map(name => name.replace('@o-lang/', '').replace(/-resolver$/, ''));
344
+
345
+ // šŸ”’ Block capability hallucinations (claims to do things outside allowlist)
346
+ const forbiddenPatterns = [
347
+ { pattern: /\b(transfer|send|wire|pay|withdraw|deposit)\b/i, capability: 'transfer' },
348
+ { pattern: /\b(create|open|close|delete)\s+(account|profile)\b/i, capability: 'account_management' },
349
+ { pattern: /\bI (can|will|am able to)\s+(transfer|pay|send)/i, capability: 'unauthorized_action' }
350
+ ];
351
+
352
+ for (const { pattern, capability } of forbiddenPatterns) {
353
+ if (pattern.test(output)) {
354
+ // āœ… Only block if capability NOT in allowlist
355
+ if (!allowedCapabilities.some(c => c.includes(capability) || c.includes('transfer'))) {
356
+ return {
357
+ passed: false,
358
+ reason: `Hallucinated "${capability}" capability (not in workflow allowlist: ${allowedCapabilities.join(', ') || 'none'})`,
359
+ detected: output.match(pattern)?.[0] || 'unknown'
360
+ };
361
+ }
362
+ }
363
+ }
364
+
365
+ return { passed: true };
366
+ }
367
+
335
368
  // -----------------------------
336
369
  // āœ… CRITICAL FIX: Resolver output unwrapping helper
337
370
  // -----------------------------
@@ -538,7 +571,7 @@ class RuntimeAPI {
538
571
  }
539
572
  });
540
573
  if (!hasDocs) {
541
- errorMessage += ` → Visit https://www.npmjs.com/search?q=%40o-lang for resolver packages\n`; // āœ… FIXED
574
+ errorMessage += ` → Visit https://www.npmjs.com/search?q=%40o-lang for resolver packages\n`; // āœ… FIXED
542
575
  }
543
576
 
544
577
  errorMessage += `\nšŸ›‘ Workflow halted to prevent unsafe data propagation to LLMs.`;
@@ -552,7 +585,7 @@ class RuntimeAPI {
552
585
  break;
553
586
  }
554
587
 
555
- case 'action': {
588
+ case 'action': {
556
589
  // šŸ”’ Interpolate workflow variables first
557
590
  let action = this._safeInterpolate(
558
591
  step.actionRaw,
@@ -600,6 +633,60 @@ class RuntimeAPI {
600
633
  const rawResult = await runResolvers(action);
601
634
  const unwrapped = this._unwrapResolverResult(rawResult);
602
635
 
636
+ // šŸ”’ KERNEL-ENFORCED: Block LLM hallucinations BEFORE saving to context
637
+ // Detect LLM resolver by action pattern (comprehensive coverage)
638
+ const isLLMAction = action.toLowerCase().includes('groq') ||
639
+ action.toLowerCase().includes('openai') ||
640
+ action.toLowerCase().includes('anthropic') ||
641
+ action.toLowerCase().includes('claude') ||
642
+ action.toLowerCase().includes('gpt') ||
643
+ action.toLowerCase().includes('gemini') ||
644
+ action.toLowerCase().includes('google') ||
645
+ action.toLowerCase().includes('llama') ||
646
+ action.toLowerCase().includes('meta') ||
647
+ action.toLowerCase().includes('mistral') ||
648
+ action.toLowerCase().includes('mixtral') ||
649
+ action.toLowerCase().includes('cohere') ||
650
+ action.toLowerCase().includes('huggingface') ||
651
+ action.toLowerCase().includes('hugging-face') ||
652
+ action.toLowerCase().includes('together') ||
653
+ action.toLowerCase().includes('perplexity') ||
654
+ action.toLowerCase().includes('fireworks') ||
655
+ action.toLowerCase().includes('bedrock') ||
656
+ action.toLowerCase().includes('azure') ||
657
+ action.toLowerCase().includes('ollama') ||
658
+ action.toLowerCase().includes('replicate') ||
659
+ action.toLowerCase().includes('deepseek') ||
660
+ action.toLowerCase().includes('qwen') ||
661
+ action.toLowerCase().includes('falcon') ||
662
+ action.toLowerCase().includes('phi') ||
663
+ action.toLowerCase().includes('gemma') ||
664
+ action.toLowerCase().includes('stablelm') ||
665
+ action.toLowerCase().includes('yi') ||
666
+ action.toLowerCase().includes('dbrx') ||
667
+ action.toLowerCase().includes('command') ||
668
+ action.toLowerCase().includes('llm'); // Catch-all fallback
669
+
670
+ // Extract actual text from resolver output (your llm-groq returns { response: "...", ... })
671
+ const llmText = unwrapped?.response || // āœ… Primary field for @o-lang/llm-groq
672
+ unwrapped?.text ||
673
+ unwrapped?.content ||
674
+ unwrapped?.answer ||
675
+ (typeof unwrapped === 'string' ? unwrapped : null);
676
+
677
+ if (isLLMAction && typeof llmText === 'string') {
678
+ const safetyCheck = this._validateLLMOutput(llmText, action);
679
+ if (!safetyCheck.passed) {
680
+ throw new Error(
681
+ `[O-Lang SAFETY] LLM hallucinated unauthorized capability:\n` +
682
+ ` → Detected: "${safetyCheck.detected}"\n` +
683
+ ` → Reason: ${safetyCheck.reason}\n` +
684
+ ` → Workflow allowlist: ${Array.from(this.allowedResolvers).join(', ')}\n` +
685
+ `\nšŸ›‘ Halting to prevent deceptive user experience.`
686
+ );
687
+ }
688
+ }
689
+
603
690
  if (step.saveAs) {
604
691
  this.context[step.saveAs] = unwrapped;
605
692
  }
@@ -627,6 +714,20 @@ class RuntimeAPI {
627
714
  const rawResult = await runResolvers(`Action ${target}`);
628
715
  const unwrapped = this._unwrapResolverResult(rawResult);
629
716
 
717
+ // šŸ”’ KERNEL-ENFORCED: Block LLM hallucinations BEFORE saving to context
718
+ if (typeof unwrapped?.output === 'string') {
719
+ const safetyCheck = this._validateLLMOutput(unwrapped.output, target);
720
+ if (!safetyCheck.passed) {
721
+ throw new Error(
722
+ `[O-Lang SAFETY] LLM hallucinated unauthorized capability:\n` +
723
+ ` → Detected: "${safetyCheck.detected}"\n` +
724
+ ` → Reason: ${safetyCheck.reason}\n` +
725
+ ` → Workflow allowlist: ${Array.from(this.allowedResolvers).join(', ')}\n` +
726
+ `\nšŸ›‘ Halting to prevent deceptive user experience.`
727
+ );
728
+ }
729
+ }
730
+
630
731
  if (step.saveAs) this.context[step.saveAs] = unwrapped;
631
732
  break;
632
733
  }