@o-lang/olang 1.2.3 ā 1.2.5
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 +1 -1
- package/src/runtime/RuntimeAPI.js +83 -16
package/package.json
CHANGED
|
@@ -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
|
|
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.`;
|
|
@@ -600,6 +633,26 @@ 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 if this was an LLM resolver by checking action pattern
|
|
638
|
+
const isLLMAction = action.toLowerCase().includes('groq') ||
|
|
639
|
+
action.toLowerCase().includes('openai') ||
|
|
640
|
+
action.toLowerCase().includes('anthropic') ||
|
|
641
|
+
action.toLowerCase().includes('llm');
|
|
642
|
+
|
|
643
|
+
if (isLLMAction && typeof unwrapped?.output === 'string') {
|
|
644
|
+
const safetyCheck = this._validateLLMOutput(unwrapped.output, action);
|
|
645
|
+
if (!safetyCheck.passed) {
|
|
646
|
+
throw new Error(
|
|
647
|
+
`[O-Lang SAFETY] LLM hallucinated unauthorized capability:\n` +
|
|
648
|
+
` ā Detected: "${safetyCheck.detected}"\n` +
|
|
649
|
+
` ā Reason: ${safetyCheck.reason}\n` +
|
|
650
|
+
` ā Workflow allowlist: ${Array.from(this.allowedResolvers).join(', ')}\n` +
|
|
651
|
+
`\nš Halting to prevent deceptive user experience.`
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
603
656
|
if (step.saveAs) {
|
|
604
657
|
this.context[step.saveAs] = unwrapped;
|
|
605
658
|
}
|
|
@@ -616,20 +669,34 @@ class RuntimeAPI {
|
|
|
616
669
|
break;
|
|
617
670
|
}
|
|
618
671
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
672
|
+
case 'ask': {
|
|
673
|
+
const target = this._safeInterpolate(step.target, this.context, 'LLM prompt');
|
|
674
|
+
|
|
675
|
+
if (/{[^}]+}/.test(target)) {
|
|
676
|
+
throw new Error(`[O-Lang] Unresolved variables in prompt: "${target}"`);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// ā
Ask ā Action happens ONLY here (runtime)
|
|
680
|
+
const rawResult = await runResolvers(`Action ${target}`);
|
|
681
|
+
const unwrapped = this._unwrapResolverResult(rawResult);
|
|
682
|
+
|
|
683
|
+
// š KERNEL-ENFORCED: Block LLM hallucinations BEFORE saving to context
|
|
684
|
+
if (typeof unwrapped?.output === 'string') {
|
|
685
|
+
const safetyCheck = this._validateLLMOutput(unwrapped.output, target);
|
|
686
|
+
if (!safetyCheck.passed) {
|
|
687
|
+
throw new Error(
|
|
688
|
+
`[O-Lang SAFETY] LLM hallucinated unauthorized capability:\n` +
|
|
689
|
+
` ā Detected: "${safetyCheck.detected}"\n` +
|
|
690
|
+
` ā Reason: ${safetyCheck.reason}\n` +
|
|
691
|
+
` ā Workflow allowlist: ${Array.from(this.allowedResolvers).join(', ')}\n` +
|
|
692
|
+
`\nš Halting to prevent deceptive user experience.`
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (step.saveAs) this.context[step.saveAs] = unwrapped;
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
633
700
|
|
|
634
701
|
case 'evolve': {
|
|
635
702
|
const { targetResolver, feedback } = step;
|