@o-lang/olang 1.2.28 → 1.2.30
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/parser/index.js +26 -0
- package/src/runtime/RuntimeAPI.js +71 -34
package/package.json
CHANGED
package/src/parser/index.js
CHANGED
|
@@ -305,6 +305,20 @@ function parseWorkflowLines(lines, filename) {
|
|
|
305
305
|
continue;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
// ✅ ADD: Set keyword (e.g., Set analysis_result = "")
|
|
309
|
+
const setMatch = line.match(/^Set\s+(\w+)\s*=\s*(.+)$/i);
|
|
310
|
+
if (setMatch) {
|
|
311
|
+
flushCurrentStep();
|
|
312
|
+
workflow.steps.push({
|
|
313
|
+
type: 'calculate',
|
|
314
|
+
stepNumber: workflow.steps.length + 1,
|
|
315
|
+
actionRaw: setMatch[2].trim(), // e.g., '""' or '"N/A"'
|
|
316
|
+
saveAs: setMatch[1].trim(), // e.g., 'analysis_result'
|
|
317
|
+
constraints: {}
|
|
318
|
+
});
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
|
|
308
322
|
// Debrief
|
|
309
323
|
const debriefMatch = line.match(/^Debrief\s+([^\s]+)\s+with\s+"([^"]*)"$/i);
|
|
310
324
|
if (debriefMatch) {
|
|
@@ -589,6 +603,18 @@ function parseBlock(lines) {
|
|
|
589
603
|
continue;
|
|
590
604
|
}
|
|
591
605
|
|
|
606
|
+
// ✅ ADD: Set keyword inside blocks (e.g., Set analysis_result = "")
|
|
607
|
+
const setMatch = line.match(/^Set\s+(\w+)\s*=\s*(.+)$/i);
|
|
608
|
+
if (setMatch) {
|
|
609
|
+
flush(); // Flush any pending step
|
|
610
|
+
steps.push({
|
|
611
|
+
type: 'calculate',
|
|
612
|
+
actionRaw: setMatch[2].trim(), // e.g., '""' or '"N/A"'
|
|
613
|
+
saveAs: setMatch[1].trim(), // e.g., 'analysis_result'
|
|
614
|
+
constraints: {}
|
|
615
|
+
});
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
592
618
|
// Fallback
|
|
593
619
|
if (current) {
|
|
594
620
|
current.actionRaw += ' ' + line; // ← PRESERVED EXACTLY (no normalizeAction)
|
|
@@ -3,7 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const crypto = require('crypto'); // ✅ CRYPTOGRAPHIC AUDIT LOGS
|
|
4
4
|
|
|
5
5
|
// ✅ O-Lang Kernel Version (Safety Logic & Governance Rules)
|
|
6
|
-
const KERNEL_VERSION = '1.2.
|
|
6
|
+
const KERNEL_VERSION = '1.2.30-alpha'; // 🔁 Update when safety rules change
|
|
7
7
|
|
|
8
8
|
class RuntimeAPI {
|
|
9
9
|
constructor({ verbose = false } = {}) {
|
|
@@ -638,17 +638,38 @@ class RuntimeAPI {
|
|
|
638
638
|
return path.split('.').reduce((o, k) => (o && o[k] !== undefined ? o[k] : undefined), obj);
|
|
639
639
|
}
|
|
640
640
|
|
|
641
|
-
|
|
641
|
+
evaluateCondition(cond, ctx) {
|
|
642
642
|
cond = cond.trim();
|
|
643
|
-
|
|
644
|
-
|
|
643
|
+
|
|
644
|
+
// ✅ 1. Handle Logical OR (|| or 'or')
|
|
645
|
+
if (/\|\||\bor\b/i.test(cond)) {
|
|
646
|
+
return cond.split(/\|\||\bor\b/i).some(c => this.evaluateCondition(c.trim(), ctx));
|
|
647
|
+
}
|
|
648
|
+
// ✅ 2. Handle Logical AND (&& or 'and')
|
|
649
|
+
if (/&&|\band\b/i.test(cond)) {
|
|
650
|
+
return cond.split(/&&|\band\b/i).every(c => this.evaluateCondition(c.trim(), ctx));
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// ✅ 3. Handle == or === (works with or without {})
|
|
654
|
+
const eqMatch = cond.match(/^(?:\{(.+)\}|(\w+))\s*===?\s*"(.*)"$/);
|
|
655
|
+
if (eqMatch) {
|
|
656
|
+
const key = eqMatch[1] || eqMatch[2];
|
|
657
|
+
return this.getNested(ctx, key) === eqMatch[3];
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// ✅ 4. Keep original O-Lang syntax
|
|
661
|
+
const oldEq = cond.match(/^\{(.+)\}\s+equals\s+"(.*)"$/);
|
|
662
|
+
if (oldEq) return this.getNested(ctx, oldEq[1]) == oldEq[2];
|
|
663
|
+
|
|
645
664
|
const gt = cond.match(/^\{(.+)\}\s+greater than\s+(\d+\.?\d*)$/);
|
|
646
665
|
if (gt) return parseFloat(this.getNested(ctx, gt[1])) > parseFloat(gt[2]);
|
|
666
|
+
|
|
647
667
|
const lt = cond.match(/^\{(.+)\}\s+less than\s+(\d+\.?\d*)$/);
|
|
648
668
|
if (lt) return parseFloat(this.getNested(ctx, lt[1])) < parseFloat(lt[2]);
|
|
669
|
+
|
|
670
|
+
// Fallback: truthy check
|
|
649
671
|
return Boolean(this.getNested(ctx, cond.replace(/\{|\}/g, '')));
|
|
650
672
|
}
|
|
651
|
-
|
|
652
673
|
mathFunctions = {
|
|
653
674
|
add: (a, b) => a + b,
|
|
654
675
|
subtract: (a, b) => a - b,
|
|
@@ -857,41 +878,57 @@ class RuntimeAPI {
|
|
|
857
878
|
if (!output || typeof output !== 'string') return { passed: true };
|
|
858
879
|
|
|
859
880
|
// ── __verified_intent takes priority ──────────────────────────────────────
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
language: 'multi'
|
|
873
|
-
};
|
|
874
|
-
}
|
|
875
|
-
}
|
|
881
|
+
const intent = this.context.__verified_intent;
|
|
882
|
+
if (intent) {
|
|
883
|
+
if (intent.prohibited_actions && Array.isArray(intent.prohibited_actions)) {
|
|
884
|
+
const lower = output.toLowerCase();
|
|
885
|
+
for (const action of intent.prohibited_actions) {
|
|
886
|
+
if (lower.includes(action.toLowerCase())) {
|
|
887
|
+
return {
|
|
888
|
+
passed: false,
|
|
889
|
+
reason: `Output violates prohibited action "${action}" defined in __verified_intent`,
|
|
890
|
+
detected: action,
|
|
891
|
+
language: 'multi'
|
|
892
|
+
};
|
|
876
893
|
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
877
896
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
897
|
+
if (intent.prohibited_topics && Array.isArray(intent.prohibited_topics)) {
|
|
898
|
+
for (const topic of intent.prohibited_topics) {
|
|
899
|
+
const isRegex = typeof topic === 'object' && topic.pattern;
|
|
900
|
+
let matched = false;
|
|
901
|
+
let detected = '';
|
|
902
|
+
|
|
903
|
+
if (isRegex) {
|
|
904
|
+
try {
|
|
905
|
+
const re = new RegExp(topic.pattern, topic.flags || 'i');
|
|
906
|
+
const match = output.match(re);
|
|
907
|
+
matched = !!match;
|
|
908
|
+
detected = match ? match[0] : topic.pattern;
|
|
909
|
+
} catch (e) {
|
|
910
|
+
this.addWarning(`Invalid prohibited_topic regex: "${topic.pattern}" — ${e.message}`);
|
|
911
|
+
continue;
|
|
889
912
|
}
|
|
913
|
+
} else {
|
|
914
|
+
matched = output.toLowerCase().includes(topic.toLowerCase());
|
|
915
|
+
detected = topic;
|
|
890
916
|
}
|
|
891
917
|
|
|
892
|
-
|
|
893
|
-
|
|
918
|
+
if (matched) {
|
|
919
|
+
return {
|
|
920
|
+
passed: false,
|
|
921
|
+
reason: `Output violates prohibited topic "${isRegex ? topic.pattern : topic}" defined in __verified_intent`,
|
|
922
|
+
detected,
|
|
923
|
+
language: 'multi'
|
|
924
|
+
};
|
|
925
|
+
}
|
|
894
926
|
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// __verified_intent present and passed — skip hardcoded patterns
|
|
930
|
+
return { passed: true };
|
|
931
|
+
}
|
|
895
932
|
|
|
896
933
|
// ── No __verified_intent — fall through to hardcoded patterns ─────────────
|
|
897
934
|
// 🔑 Extract allowed capabilities from workflow allowlist
|